summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt5
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt6
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt12
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml1
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt175
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java150
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt5
13 files changed, 376 insertions, 39 deletions
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index f79b328190dd..5f1bb83715c2 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -89,6 +89,11 @@ class TextAnimator(
var y: Float = 0f
/**
+ * The current line of text being drawn, in a multi-line TextView.
+ */
+ var lineNo: Int = 0
+
+ /**
* Mutable text size of the glyph in pixels.
*/
var textSize: Float = 0f
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index d427a57f3b87..0448c818f765 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -244,7 +244,7 @@ class TextInterpolator(
canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat())
run.fontRuns.forEach { fontRun ->
- drawFontRun(canvas, run, fontRun, tmpPaint)
+ drawFontRun(canvas, run, fontRun, lineNo, tmpPaint)
}
} finally {
canvas.restore()
@@ -349,7 +349,7 @@ class TextInterpolator(
var glyphFilter: GlyphCallback? = null
// Draws single font run.
- private fun drawFontRun(c: Canvas, line: Run, run: FontRun, paint: Paint) {
+ private fun drawFontRun(c: Canvas, line: Run, run: FontRun, lineNo: Int, paint: Paint) {
var arrayIndex = 0
val font = fontInterpolator.lerp(run.baseFont, run.targetFont, progress)
@@ -368,11 +368,13 @@ class TextInterpolator(
tmpGlyph.font = font
tmpGlyph.runStart = run.start
tmpGlyph.runLength = run.end - run.start
+ tmpGlyph.lineNo = lineNo
tmpPaintForGlyph.set(paint)
var prevStart = run.start
for (i in run.start until run.end) {
+ tmpGlyph.glyphIndex = i
tmpGlyph.glyphId = line.glyphIds[i]
tmpGlyph.x = MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
tmpGlyph.y = MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
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 1e74c3d68efc..dabb43b6074d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -14,6 +14,7 @@
package com.android.systemui.plugins
import android.content.res.Resources
+import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.View
import com.android.systemui.plugins.annotations.ProvidesInterface
@@ -114,6 +115,17 @@ 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) { }
+
+ /**
+ * Whether this clock has a custom position update animation. If true, the keyguard will call
+ * `onPositionUpdated` to notify the clock of a position update animation. If false, a default
+ * animation will be used (e.g. a simple translation).
+ */
+ val hasCustomPositionUpdatedAnimation
+ get() = false
}
/** Events that have specific data about the related face */
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 3ad7c8c4369c..d64587dcf362 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -37,6 +37,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/keyguard_large_clock_top_margin"
+ android:clipChildren="false"
android:visibility="gone" />
<!-- Not quite optimal but needed to translate these items as a group. The
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index f3f6507836b1..1cf7c503a508 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -20,16 +20,15 @@ import android.annotation.ColorInt
import android.annotation.FloatRange
import android.annotation.IntRange
import android.annotation.SuppressLint
-import android.app.compat.ChangeIdStateCache.invalidate
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.widget.TextView
-import com.android.internal.R.attr.contentDescription
-import com.android.internal.R.attr.format
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.animation.GlyphCallback
import com.android.systemui.animation.Interpolators
@@ -39,6 +38,8 @@ 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)
@@ -316,7 +317,24 @@ class AnimatableClockView @JvmOverloads constructor(
)
}
- private val glyphFilter: GlyphCallback? = null // Add text animation tweak here.
+ // The offset of each glyph from where it should be.
+ private var glyphOffsets = mutableListOf(0.0f, 0.0f, 0.0f, 0.0f)
+
+ private var lastSeenAnimationProgress = 1.0f
+
+ // If the animation is being reversed, the target offset for each glyph for the "stop".
+ private var animationCancelStartPosition = mutableListOf(0.0f, 0.0f, 0.0f, 0.0f)
+ private var animationCancelStopPosition = 0.0f
+
+ // Whether the currently playing animation needed a stop (and thus, is shortened).
+ private var currentAnimationNeededStop = false
+
+ private val glyphFilter: GlyphCallback = { positionedGlyph, _ ->
+ val offset = positionedGlyph.lineNo * DIGITS_PER_LINE + positionedGlyph.glyphIndex
+ if (offset < glyphOffsets.size) {
+ positionedGlyph.x += glyphOffsets[offset]
+ }
+ }
/**
* Set text style with an optional animation.
@@ -432,6 +450,124 @@ 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
+
+ // 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)
+
+ 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
+ // 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])
+ }
+ }
+
+ 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.
// This is an optimization to ensure we only recompute the patterns when the inputs change.
private object Patterns {
@@ -469,5 +605,36 @@ class AnimatableClockView @JvmOverloads constructor(
private const val APPEAR_ANIM_DURATION: Long = 350
private const val CHARGE_ANIM_DURATION_PHASE_0: Long = 500
private const val CHARGE_ANIM_DURATION_PHASE_1: Long = 1000
+
+ // Constants for the animation
+ private val MOVE_INTERPOLATOR = Interpolators.STANDARD
+
+ // Calculate the positions of all of the digits...
+ // Offset each digit by, say, 0.1
+ // This means that each digit needs to move over a slice of "fractions", i.e. digit 0 should
+ // move from 0.0 - 0.7, digit 1 from 0.1 - 0.8, digit 2 from 0.2 - 0.9, and digit 3
+ // from 0.3 - 1.0.
+ 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 = 0.4f
+
+ // 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
+ // the delay for each digit (hour[0], hour[1], minute[0], minute[1]), to be multiplied
+ // by delayMultiplier.
+ private val MOVE_LEFT_DELAYS = listOf(0, 1, 2, 3)
+ private val MOVE_RIGHT_DELAYS = listOf(1, 0, 3, 2)
+
+ // How much delay to apply to each subsequent digit. This is measured in terms of "fraction"
+ // (i.e. a value of 0.1 would cause a digit to wait until fraction had hit 0.1, or 0.2 etc
+ // before moving).
+ private const val MOVE_DIGIT_STEP = 0.1f
+
+ // 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)
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index b88795157a43..6fd61daee6ff 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -16,6 +16,7 @@ package com.android.systemui.shared.clocks
import android.content.Context
import android.content.res.Resources
import android.graphics.Color
+import android.graphics.Rect
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater
@@ -130,6 +131,10 @@ class DefaultClockController(
lp.topMargin = (-0.5f * view.bottom).toInt()
view.setLayoutParams(lp)
}
+
+ fun moveForSplitShade(fromRect: Rect, toRect: Rect, fraction: Float) {
+ view.moveForSplitShade(fromRect, toRect, fraction)
+ }
}
inner class DefaultClockEvents : ClockEvents {
@@ -209,6 +214,13 @@ class DefaultClockController(
clocks.forEach { it.animateDoze(dozeState.isActive, !hasJumped) }
}
}
+
+ override fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) {
+ largeClock.moveForSplitShade(fromRect, toRect, fraction)
+ }
+
+ override val hasCustomPositionUpdatedAnimation: Boolean
+ get() = true
}
private class AnimationState(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 202134b699d7..35eecdf27f48 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -40,6 +40,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.plugins.ClockAnimations;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.clocks.ClockRegistry;
@@ -404,5 +405,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
clock.dump(pw);
}
}
+
+ /** Gets the animations for the current clock. */
+ public ClockAnimations getClockAnimations() {
+ return getClock().getAnimations();
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index e9f06eddf261..784974778af5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -20,6 +20,7 @@ import android.graphics.Rect;
import android.util.Slog;
import com.android.keyguard.KeyguardClockSwitch.ClockSize;
+import com.android.systemui.plugins.ClockAnimations;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -232,4 +233,9 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mView.setClipBounds(null);
}
}
+
+ /** Gets the animations for the current clock. */
+ public ClockAnimations getClockAnimations() {
+ return mKeyguardClockSwitchController.getClockAnimations();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 6531a32979b8..9415d6a90abc 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -143,6 +143,12 @@ public class Flags {
// TODO(b/254513102): Tracking Bug
public static final ReleasedFlag USER_CONTROLLER_USES_INTERACTOR = new ReleasedFlag(211);
+ /**
+ * Whether the clock on a wide lock screen should use the new "stepping" animation for moving
+ * the digits when the clock moves.
+ */
+ public static final UnreleasedFlag STEP_CLOCK_ANIMATION = new UnreleasedFlag(212);
+
/***************************************/
// 300 - power menu
// TODO(b/254512600): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index c1afd59a1e22..54236833246d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -79,7 +79,10 @@ import android.os.UserManager;
import android.os.VibrationEffect;
import android.provider.Settings;
import android.transition.ChangeBounds;
+import android.transition.Transition;
import android.transition.TransitionManager;
+import android.transition.TransitionSet;
+import android.transition.TransitionValues;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.MathUtils;
@@ -1667,9 +1670,40 @@ public final class NotificationPanelViewController {
// horizontally properly.
transition.excludeTarget(R.id.status_view_media_container, true);
}
+
transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- TransitionManager.beginDelayedTransition(mNotificationContainerParent, transition);
+
+ boolean customClockAnimation =
+ mKeyguardStatusViewController.getClockAnimations() != null
+ && mKeyguardStatusViewController.getClockAnimations()
+ .getHasCustomPositionUpdatedAnimation();
+
+ if (mFeatureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION) && customClockAnimation) {
+ // Find the clock, so we can exclude it from this transition.
+ FrameLayout clockContainerView =
+ mView.findViewById(R.id.lockscreen_clock_view_large);
+ View clockView = clockContainerView.getChildAt(0);
+
+ transition.excludeTarget(clockView, /* exclude= */ true);
+
+ TransitionSet set = new TransitionSet();
+ set.addTransition(transition);
+
+ SplitShadeTransitionAdapter adapter =
+ new SplitShadeTransitionAdapter(mKeyguardStatusViewController);
+
+ // Use linear here, so the actual clock can pick its own interpolator.
+ adapter.setInterpolator(Interpolators.LINEAR);
+ adapter.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ adapter.addTarget(clockView);
+ set.addTransition(adapter);
+
+ TransitionManager.beginDelayedTransition(mNotificationContainerParent, set);
+ } else {
+ TransitionManager.beginDelayedTransition(
+ mNotificationContainerParent, transition);
+ }
}
constraintSet.applyTo(mNotificationContainerParent);
@@ -4166,8 +4200,8 @@ public final class NotificationPanelViewController {
/**
* Sets the dozing state.
*
- * @param dozing {@code true} when dozing.
- * @param animate if transition should be animated.
+ * @param dozing {@code true} when dozing.
+ * @param animate if transition should be animated.
*/
public void setDozing(boolean dozing, boolean animate) {
if (dozing == mDozing) return;
@@ -4307,35 +4341,35 @@ public final class NotificationPanelViewController {
/**
* Starts fold to AOD animation.
*
- * @param startAction invoked when the animation starts.
- * @param endAction invoked when the animation finishes, also if it was cancelled.
+ * @param startAction invoked when the animation starts.
+ * @param endAction invoked when the animation finishes, also if it was cancelled.
* @param cancelAction invoked when the animation is cancelled, before endAction.
*/
public void startFoldToAodAnimation(Runnable startAction, Runnable endAction,
Runnable cancelAction) {
mView.animate()
- .translationX(0)
- .alpha(1f)
- .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
- .setInterpolator(EMPHASIZED_DECELERATE)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- startAction.run();
- }
+ .translationX(0)
+ .alpha(1f)
+ .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
+ .setInterpolator(EMPHASIZED_DECELERATE)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ startAction.run();
+ }
- @Override
- public void onAnimationCancel(Animator animation) {
- cancelAction.run();
- }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ cancelAction.run();
+ }
- @Override
- public void onAnimationEnd(Animator animation) {
- endAction.run();
- }
- }).setUpdateListener(anim -> {
- mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
- }).start();
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endAction.run();
+ }
+ }).setUpdateListener(anim -> {
+ mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
+ }).start();
}
/**
@@ -4695,8 +4729,10 @@ public final class NotificationPanelViewController {
/**
* Maybe vibrate as panel is opened.
*
- * @param openingWithTouch Whether the panel is being opened with touch. If the panel is instead
- * being opened programmatically (such as by the open panel gesture), we always play haptic.
+ * @param openingWithTouch Whether the panel is being opened with touch. If the panel is
+ * instead
+ * being opened programmatically (such as by the open panel gesture), we
+ * always play haptic.
*/
private void maybeVibrateOnOpening(boolean openingWithTouch) {
if (mVibrateOnOpening) {
@@ -4861,10 +4897,12 @@ public final class NotificationPanelViewController {
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
animator.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
+
@Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
+
@Override
public void onAnimationEnd(Animator animation) {
mIsSpringBackAnimation = false;
@@ -4912,7 +4950,7 @@ public final class NotificationPanelViewController {
if (isNaN(h)) {
Log.wtf(TAG, "ExpandedHeight set to NaN");
}
- mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
+ mNotificationShadeWindowController.batchApplyWindowLayoutParams(() -> {
if (mExpandLatencyTracking && h != 0f) {
DejankUtils.postAfterTraversal(
() -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
@@ -5103,7 +5141,7 @@ public final class NotificationPanelViewController {
/**
* Create an animator that can also overshoot
*
- * @param targetHeight the target height
+ * @param targetHeight the target height
* @param overshootAmount the amount of overshoot desired
*/
private ValueAnimator createHeightAnimator(float targetHeight, float overshootAmount) {
@@ -5900,7 +5938,7 @@ public final class NotificationPanelViewController {
public final class TouchHandler implements View.OnTouchListener {
private long mLastTouchDownTime = -1L;
- /** @see ViewGroup#onInterceptTouchEvent(MotionEvent) */
+ /** @see ViewGroup#onInterceptTouchEvent(MotionEvent) */
public boolean onInterceptTouchEvent(MotionEvent event) {
if (SPEW_LOGCAT) {
Log.v(TAG,
@@ -6099,7 +6137,7 @@ public final class NotificationPanelViewController {
mShadeLog.logMotionEvent(event, "onTouch: touch ignored due to instant expanding");
return false;
}
- if (mTouchDisabled && event.getActionMasked() != MotionEvent.ACTION_CANCEL) {
+ if (mTouchDisabled && event.getActionMasked() != MotionEvent.ACTION_CANCEL) {
mShadeLog.logMotionEvent(event, "onTouch: non-cancel action, touch disabled");
return false;
}
@@ -6256,4 +6294,54 @@ public final class NotificationPanelViewController {
loadDimens();
}
}
+
+ static class SplitShadeTransitionAdapter extends Transition {
+ private static final String PROP_BOUNDS = "splitShadeTransitionAdapter:bounds";
+ private static final String[] TRANSITION_PROPERTIES = { PROP_BOUNDS };
+
+ private final KeyguardStatusViewController mController;
+
+ SplitShadeTransitionAdapter(KeyguardStatusViewController controller) {
+ mController = controller;
+ }
+
+ 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);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+
+ Rect from = (Rect) startValues.values.get(PROP_BOUNDS);
+ Rect to = (Rect) endValues.values.get(PROP_BOUNDS);
+
+ anim.addUpdateListener(
+ animation -> mController.getClockAnimations().onPositionUpdated(
+ from, to, animation.getAnimatedFraction()));
+
+ return anim;
+ }
+
+ @Override
+ public String[] getTransitionProperties() {
+ return TRANSITION_PROPERTIES;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index bb03a47e025c..627d738a895f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;
@@ -45,6 +46,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.plugins.ClockAnimations;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.clocks.AnimatableClockView;
@@ -262,6 +264,19 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
verify(mView).switchToClock(KeyguardClockSwitch.SMALL, /* animate */ true);
}
+ @Test
+ public void testGetClockAnimationsForwardsToClock() {
+ ClockController mockClockController = mock(ClockController.class);
+ ClockAnimations mockClockAnimations = mock(ClockAnimations.class);
+ when(mClockEventController.getClock()).thenReturn(mockClockController);
+ when(mockClockController.getAnimations()).thenReturn(mockClockAnimations);
+
+ Rect r1 = new Rect(1, 2, 3, 4);
+ Rect r2 = new Rect(5, 6, 7, 8);
+ mController.getClockAnimations().onPositionUpdated(r1, r2, 0.2f);
+ verify(mockClockAnimations).onPositionUpdated(r1, r2, 0.2f);
+ }
+
private void verifyAttachment(VerificationMode times) {
verify(mClockRegistry, times).registerClockChangeListener(
any(ClockRegistry.ClockChangeListener.class));
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 4dcaa7cf8c09..c94c97c9b638 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -16,12 +16,16 @@
package com.android.keyguard;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.graphics.Rect;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ClockAnimations;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -108,4 +112,16 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
configurationListenerArgumentCaptor.getValue().onLocaleListChanged();
verify(mKeyguardClockSwitchController).onLocaleListChanged();
}
+
+ @Test
+ public void getClockAnimations_forwardsToClockSwitch() {
+ ClockAnimations mockClockAnimations = mock(ClockAnimations.class);
+ when(mKeyguardClockSwitchController.getClockAnimations()).thenReturn(mockClockAnimations);
+
+ Rect r1 = new Rect(1, 2, 3, 4);
+ Rect r2 = new Rect(5, 6, 7, 8);
+ mController.getClockAnimations().onPositionUpdated(r1, r2, 0.3f);
+
+ verify(mockClockAnimations).onPositionUpdated(r1, r2, 0.3f);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
index eb34561d15a0..cc45cf88fa18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
@@ -22,6 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.TextAnimator
+import com.android.systemui.util.mockito.any
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -55,7 +56,7 @@ class AnimatableClockViewTest : SysuiTestCase() {
clockView.animateAppearOnLockscreen()
clockView.measure(50, 50)
- verify(mockTextAnimator).glyphFilter = null
+ verify(mockTextAnimator).glyphFilter = any()
verify(mockTextAnimator).setTextStyle(300, -1.0f, 200, false, 350L, null, 0L, null)
verifyNoMoreInteractions(mockTextAnimator)
}
@@ -66,7 +67,7 @@ class AnimatableClockViewTest : SysuiTestCase() {
clockView.measure(50, 50)
clockView.animateAppearOnLockscreen()
- verify(mockTextAnimator, times(2)).glyphFilter = null
+ verify(mockTextAnimator, times(2)).glyphFilter = any()
verify(mockTextAnimator).setTextStyle(100, -1.0f, 200, false, 0L, null, 0L, null)
verify(mockTextAnimator).setTextStyle(300, -1.0f, 200, true, 350L, null, 0L, null)
verifyNoMoreInteractions(mockTextAnimator)