Merge changes Ia5accb74,Ic4d7def4,I30bd56fc into sc-v2-dev am: 7339a87bf3
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16357603
Change-Id: I31d6bbad7f8c03f58ba8b6193aba944f9cdccbbf
diff --git a/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_x.xml b/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_x.xml
deleted file mode 100644
index 620dd48..0000000
--- a/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_x.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0, 0 C 0.1217, 0.0462, 0.15, 0.4686, 0.1667, 0.66 C 0.1834, 0.8878, 0.1667, 1, 1, 1" />
\ No newline at end of file
diff --git a/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_y.xml b/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_y.xml
deleted file mode 100644
index a268abc..0000000
--- a/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_y.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1" />
\ No newline at end of file
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index fb80f1c..a0d335d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -21,6 +21,7 @@
import android.app.PendingIntent
import android.app.TaskInfo
import android.graphics.Matrix
+import android.graphics.Path
import android.graphics.Rect
import android.graphics.RectF
import android.os.Looper
@@ -34,6 +35,7 @@
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import android.view.animation.Interpolator
import android.view.animation.PathInterpolator
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
@@ -45,16 +47,46 @@
* A class that allows activities to be started in a seamless way from a view that is transforming
* nicely into the starting window.
*/
-class ActivityLaunchAnimator(private val launchAnimator: LaunchAnimator) {
+class ActivityLaunchAnimator(
+ private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS)
+) {
companion object {
+ @JvmField
+ val TIMINGS = LaunchAnimator.Timings(
+ totalDuration = 500L,
+ contentBeforeFadeOutDelay = 0L,
+ contentBeforeFadeOutDuration = 150L,
+ contentAfterFadeInDelay = 150L,
+ contentAfterFadeInDuration = 183L
+ )
+
+ val INTERPOLATORS = LaunchAnimator.Interpolators(
+ positionInterpolator = Interpolators.EMPHASIZED,
+ positionXInterpolator = createPositionXInterpolator(),
+ contentBeforeFadeOutInterpolator = Interpolators.LINEAR_OUT_SLOW_IN,
+ contentAfterFadeInInterpolator = PathInterpolator(0f, 0f, 0.6f, 1f)
+ )
+
+ /** Durations & interpolators for the navigation bar fading in & out. */
private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
- private const val ANIMATION_DELAY_NAV_FADE_IN =
- LaunchAnimator.ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN
+ private val ANIMATION_DELAY_NAV_FADE_IN =
+ TIMINGS.totalDuration - ANIMATION_DURATION_NAV_FADE_IN
+
+ private val NAV_FADE_IN_INTERPOLATOR = Interpolators.STANDARD_DECELERATE
+ private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
+
+ /** The time we wait before timing out the remote animation after starting the intent. */
private const val LAUNCH_TIMEOUT = 1000L
- private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f)
- private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
+ private fun createPositionXInterpolator(): Interpolator {
+ val path = Path().apply {
+ moveTo(0f, 0f)
+ cubicTo(0.1217f, 0.0462f, 0.15f, 0.4686f, 0.1667f, 0.66f)
+ cubicTo(0.1834f, 0.8878f, 0.1667f, 1f, 1f, 1f)
+ }
+ return PathInterpolator(path)
+ }
}
/**
@@ -107,8 +139,8 @@
val animationAdapter = if (!hideKeyguardWithAnimation) {
RemoteAnimationAdapter(
runner,
- LaunchAnimator.ANIMATION_DURATION,
- LaunchAnimator.ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
+ TIMINGS.totalDuration,
+ TIMINGS.totalDuration - 150 /* statusBarTransitionDelay */
)
} else {
null
@@ -448,7 +480,7 @@
state: LaunchAnimator.State,
linearProgress: Float
) {
- val fadeInProgress = LaunchAnimator.getProgress(linearProgress,
+ val fadeInProgress = LaunchAnimator.getProgress(TIMINGS, linearProgress,
ANIMATION_DELAY_NAV_FADE_IN, ANIMATION_DURATION_NAV_FADE_OUT)
val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(navigationBar.leash)
@@ -463,7 +495,7 @@
.withWindowCrop(windowCrop)
.withVisibility(true)
} else {
- val fadeOutProgress = LaunchAnimator.getProgress(linearProgress, 0,
+ val fadeOutProgress = LaunchAnimator.getProgress(TIMINGS, linearProgress, 0,
ANIMATION_DURATION_NAV_FADE_OUT)
params.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress))
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index de82ebd..066e169 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -20,7 +20,6 @@
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.Dialog
-import android.content.Context
import android.graphics.Color
import android.graphics.Rect
import android.os.Looper
@@ -28,10 +27,11 @@
import android.util.Log
import android.util.MathUtils
import android.view.GhostView
+import android.view.SurfaceControl
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewTreeObserver.OnPreDrawListener
+import android.view.ViewRootImpl
import android.view.WindowManager
import android.widget.FrameLayout
import kotlin.math.roundToInt
@@ -42,12 +42,20 @@
* A class that allows dialogs to be started in a seamless way from a view that is transforming
* nicely into the starting dialog.
*/
-class DialogLaunchAnimator(
- private val context: Context,
- private val launchAnimator: LaunchAnimator,
- private val dreamManager: IDreamManager
+class DialogLaunchAnimator @JvmOverloads constructor(
+ private val dreamManager: IDreamManager,
+ private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
+ private var isForTesting: Boolean = false
) {
private companion object {
+ private val TIMINGS = ActivityLaunchAnimator.TIMINGS
+
+ // We use the same interpolator for X and Y axis to make sure the dialog does not move out
+ // of the screen bounds during the animation.
+ private val INTERPOLATORS = ActivityLaunchAnimator.INTERPOLATORS.copy(
+ positionXInterpolator = ActivityLaunchAnimator.INTERPOLATORS.positionInterpolator
+ )
+
private val TAG_LAUNCH_ANIMATION_RUNNING = R.id.launch_animation_running
}
@@ -96,14 +104,14 @@
animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
val animatedDialog = AnimatedDialog(
- context,
launchAnimator,
dreamManager,
animateFrom,
onDialogDismissed = { openedDialogs.remove(it) },
dialog = dialog,
animateBackgroundBoundsChange,
- animatedParent
+ animatedParent,
+ isForTesting
)
openedDialogs.add(animatedDialog)
@@ -157,7 +165,6 @@
}
private class AnimatedDialog(
- private val context: Context,
private val launchAnimator: LaunchAnimator,
private val dreamManager: IDreamManager,
@@ -174,10 +181,16 @@
val dialog: Dialog,
/** Whether we should animate the dialog background when its bounds change. */
- private val animateBackgroundBoundsChange: Boolean,
+ animateBackgroundBoundsChange: Boolean,
/** Launch animation corresponding to the parent [AnimatedDialog]. */
- private val parentAnimatedDialog: AnimatedDialog? = null
+ private val parentAnimatedDialog: AnimatedDialog? = null,
+
+ /**
+ * Whether we are currently running in a test, in which case we need to disable
+ * synchronization.
+ */
+ private val isForTesting: Boolean
) {
/**
* The DecorView of this dialog window.
@@ -266,14 +279,14 @@
// and the view that we added so that we can dismiss the dialog when this view is
// clicked. This is necessary because DecorView overrides onTouchEvent and therefore we
// can't set the click listener directly on the (now fullscreen) DecorView.
- val fullscreenTransparentBackground = FrameLayout(context)
+ val fullscreenTransparentBackground = FrameLayout(dialog.context)
decorView.addView(
fullscreenTransparentBackground,
0 /* index */,
FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
)
- val dialogContentWithBackground = FrameLayout(context)
+ val dialogContentWithBackground = FrameLayout(dialog.context)
dialogContentWithBackground.background = decorView.background
// Make the window background transparent. Note that setting the window (or DecorView)
@@ -365,59 +378,77 @@
// Show the dialog.
dialog.show()
- // Add a temporary touch surface ghost as soon as the window is ready to draw. This
- // temporary ghost will be drawn together with the touch surface, but in the dialog
- // window. Once it is drawn, we will make the touch surface invisible, and then start the
- // animation. We do all this synchronization to avoid flicker that would occur if we made
- // the touch surface invisible too early (before its ghost is drawn), leading to one or more
- // frames with a hole instead of the touch surface (or its ghost).
- decorView.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- decorView.viewTreeObserver.removeOnPreDrawListener(this)
- addTemporaryTouchSurfaceGhost()
- return true
- }
- })
- decorView.invalidate()
+ addTouchSurfaceGhost()
}
- private fun addTemporaryTouchSurfaceGhost() {
+ private fun addTouchSurfaceGhost() {
+ if (decorView.viewRootImpl == null) {
+ // Make sure that we have access to the dialog view root to synchronize the creation of
+ // the ghost.
+ decorView.post(::addTouchSurfaceGhost)
+ return
+ }
+
// Create a ghost of the touch surface (which will make the touch surface invisible) and add
- // it to the dialog. We will wait for this ghost to be drawn before starting the animation.
- val ghost = GhostView.addGhost(touchSurface, decorView)
-
- // The ghost of the touch surface was just created, so the touch surface was made invisible.
- // We make it visible again until the ghost is actually drawn.
- touchSurface.visibility = View.VISIBLE
-
- // Wait for the ghost to be drawn before continuing.
- ghost.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- ghost.viewTreeObserver.removeOnPreDrawListener(this)
- onTouchSurfaceGhostDrawn()
- return true
- }
+ // it to the host dialog. We trigger a one off synchronization to make sure that this is
+ // done in sync between the two different windows.
+ synchronizeNextDraw(then = {
+ isTouchSurfaceGhostDrawn = true
+ maybeStartLaunchAnimation()
})
- ghost.invalidate()
+ GhostView.addGhost(touchSurface, decorView)
+
+ // The ghost of the touch surface was just created, so the touch surface is currently
+ // invisible. We need to make sure that it stays invisible as long as the dialog is shown or
+ // animating.
+ (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
}
- private fun onTouchSurfaceGhostDrawn() {
- // Make the touch surface invisible and make sure that it stays invisible as long as the
- // dialog is shown or animating.
- touchSurface.visibility = View.INVISIBLE
- (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
+ /**
+ * Synchronize the next draw of the touch surface and dialog view roots so that they are
+ * performed at the same time, in the same transaction. This is necessary to make sure that the
+ * ghost of the touch surface is drawn at the same time as the touch surface is made invisible
+ * (or inversely, removed from the UI when the touch surface is made visible).
+ */
+ private fun synchronizeNextDraw(then: () -> Unit) {
+ if (isForTesting || !touchSurface.isAttachedToWindow || touchSurface.viewRootImpl == null ||
+ !decorView.isAttachedToWindow || decorView.viewRootImpl == null) {
+ // No need to synchronize if either the touch surface or dialog view is not attached
+ // to a window.
+ then()
+ return
+ }
- // Add a pre draw listener to (maybe) start the animation once the touch surface is
- // actually invisible.
- touchSurface.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- touchSurface.viewTreeObserver.removeOnPreDrawListener(this)
- isTouchSurfaceGhostDrawn = true
- maybeStartLaunchAnimation()
- return true
+ // Consume the next frames of both view roots to make sure the ghost view is drawn at
+ // exactly the same time as when the touch surface is made invisible.
+ var remainingTransactions = 0
+ val mergedTransactions = SurfaceControl.Transaction()
+
+ fun onTransaction(transaction: SurfaceControl.Transaction?) {
+ remainingTransactions--
+ transaction?.let { mergedTransactions.merge(it) }
+
+ if (remainingTransactions == 0) {
+ mergedTransactions.apply()
+ then()
}
- })
- touchSurface.invalidate()
+ }
+
+ fun consumeNextDraw(viewRootImpl: ViewRootImpl) {
+ if (viewRootImpl.consumeNextDraw(::onTransaction)) {
+ remainingTransactions++
+
+ // Make sure we trigger a traversal.
+ viewRootImpl.view.invalidate()
+ }
+ }
+
+ consumeNextDraw(touchSurface.viewRootImpl)
+ consumeNextDraw(decorView.viewRootImpl)
+
+ if (remainingTransactions == 0) {
+ then()
+ }
}
private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
@@ -483,7 +514,7 @@
private fun onDialogDismissed() {
if (Looper.myLooper() != Looper.getMainLooper()) {
- context.mainExecutor.execute { onDialogDismissed() }
+ dialog.context.mainExecutor.execute { onDialogDismissed() }
return
}
@@ -556,25 +587,12 @@
.removeOnLayoutChangeListener(backgroundLayoutListener)
}
- // The animated ghost was just removed. We create a temporary ghost that will be
- // removed only once we draw the touch surface, to avoid flickering that would
- // happen when removing the ghost too early (before the touch surface is drawn).
- GhostView.addGhost(touchSurface, decorView)
-
- touchSurface.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- touchSurface.viewTreeObserver.removeOnPreDrawListener(this)
-
- // Now that the touch surface was drawn, we can remove the temporary ghost
- // and instantly dismiss the dialog.
- GhostView.removeGhost(touchSurface)
- onAnimationFinished(true /* instantDismiss */)
- onDialogDismissed(this@AnimatedDialog)
-
- return true
- }
+ // Make sure that the removal of the ghost and making the touch surface visible is
+ // done at the same time.
+ synchronizeNextDraw(then = {
+ onAnimationFinished(true /* instantDismiss */)
+ onDialogDismissed(this@AnimatedDialog)
})
- touchSurface.invalidate()
}
)
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
index 3bf6c5e..ebe96eb 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
@@ -27,25 +27,19 @@
import android.util.MathUtils
import android.view.View
import android.view.ViewGroup
-import android.view.animation.AnimationUtils
-import android.view.animation.PathInterpolator
+import android.view.animation.Interpolator
+import com.android.systemui.animation.Interpolators.LINEAR
import kotlin.math.roundToInt
private const val TAG = "LaunchAnimator"
/** A base class to animate a window launch (activity or dialog) from a view . */
-class LaunchAnimator @JvmOverloads constructor(
- context: Context,
- private val isForTesting: Boolean = false
+class LaunchAnimator(
+ private val timings: Timings,
+ private val interpolators: Interpolators
) {
companion object {
internal const val DEBUG = false
- const val ANIMATION_DURATION = 500L
- private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L
- private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L
- private const val ANIMATION_DELAY_FADE_IN_WINDOW = ANIMATION_DURATION_FADE_OUT_CONTENT
-
- private val WINDOW_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0.6f, 1f)
private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
/**
@@ -53,23 +47,20 @@
* sub-animation starting [delay] ms after the launch animation and that lasts [duration].
*/
@JvmStatic
- fun getProgress(linearProgress: Float, delay: Long, duration: Long): Float {
+ fun getProgress(
+ timings: Timings,
+ linearProgress: Float,
+ delay: Long,
+ duration: Long
+ ): Float {
return MathUtils.constrain(
- (linearProgress * ANIMATION_DURATION - delay) / duration,
+ (linearProgress * timings.totalDuration - delay) / duration,
0.0f,
1.0f
)
}
}
- /** The interpolator used for the width, height, Y position and corner radius. */
- private val animationInterpolator = AnimationUtils.loadInterpolator(context,
- R.interpolator.launch_animation_interpolator_y)
-
- /** The interpolator used for the X position. */
- private val animationInterpolatorX = AnimationUtils.loadInterpolator(context,
- R.interpolator.launch_animation_interpolator_x)
-
private val launchContainerLocation = IntArray(2)
private val cornerRadii = FloatArray(8)
@@ -159,6 +150,45 @@
fun cancel()
}
+ /** The timings (durations and delays) used by this animator. */
+ class Timings(
+ /** The total duration of the animation. */
+ val totalDuration: Long,
+
+ /** The time to wait before fading out the expanding content. */
+ val contentBeforeFadeOutDelay: Long,
+
+ /** The duration of the expanding content fade out. */
+ val contentBeforeFadeOutDuration: Long,
+
+ /**
+ * The time to wait before fading in the expanded content (usually an activity or dialog
+ * window).
+ */
+ val contentAfterFadeInDelay: Long,
+
+ /** The duration of the expanded content fade in. */
+ val contentAfterFadeInDuration: Long
+ )
+
+ /** The interpolators used by this animator. */
+ data class Interpolators(
+ /** The interpolator used for the Y position, width, height and corner radius. */
+ val positionInterpolator: Interpolator,
+
+ /**
+ * The interpolator used for the X position. This can be different than
+ * [positionInterpolator] to create an arc-path during the animation.
+ */
+ val positionXInterpolator: Interpolator = positionInterpolator,
+
+ /** The interpolator used when fading out the expanding content. */
+ val contentBeforeFadeOutInterpolator: Interpolator,
+
+ /** The interpolator used when fading in the expanded content. */
+ val contentAfterFadeInInterpolator: Interpolator
+ )
+
/**
* Start a launch animation controlled by [controller] towards [endState]. An intermediary
* layer with [windowBackgroundColor] will fade in then fade out above the expanding view, and
@@ -221,8 +251,8 @@
// Update state.
val animator = ValueAnimator.ofFloat(0f, 1f)
- animator.duration = if (isForTesting) 0 else ANIMATION_DURATION
- animator.interpolator = Interpolators.LINEAR
+ animator.duration = timings.totalDuration
+ animator.interpolator = LINEAR
val launchContainerOverlay = launchContainer.overlay
var cancelled = false
@@ -260,8 +290,8 @@
// TODO(b/184121838): Use reverse interpolators to get the same path/arc as the non
// reversed animation.
val linearProgress = animation.animatedFraction
- val progress = animationInterpolator.getInterpolation(linearProgress)
- val xProgress = animationInterpolatorX.getInterpolation(linearProgress)
+ val progress = interpolators.positionInterpolator.getInterpolation(linearProgress)
+ val xProgress = interpolators.positionXInterpolator.getInterpolation(linearProgress)
val xCenter = MathUtils.lerp(startCenterX, endCenterX, xProgress)
val halfWidth = MathUtils.lerp(startWidth, endWidth, progress) / 2f
@@ -278,7 +308,12 @@
// The expanding view can/should be hidden once it is completely covered by the opening
// window.
- state.visible = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT) < 1
+ state.visible = getProgress(
+ timings,
+ linearProgress,
+ timings.contentBeforeFadeOutDelay,
+ timings.contentBeforeFadeOutDuration
+ ) < 1
applyStateToWindowBackgroundLayer(
windowBackgroundLayer,
@@ -337,14 +372,25 @@
// We first fade in the background layer to hide the expanding view, then fade it out
// with SRC mode to draw a hole punch in the status bar and reveal the opening window.
- val fadeInProgress = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT)
+ val fadeInProgress = getProgress(
+ timings,
+ linearProgress,
+ timings.contentBeforeFadeOutDelay,
+ timings.contentBeforeFadeOutDuration
+ )
if (fadeInProgress < 1) {
- val alpha = Interpolators.LINEAR_OUT_SLOW_IN.getInterpolation(fadeInProgress)
+ val alpha =
+ interpolators.contentBeforeFadeOutInterpolator.getInterpolation(fadeInProgress)
drawable.alpha = (alpha * 0xFF).roundToInt()
} else {
val fadeOutProgress = getProgress(
- linearProgress, ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW)
- val alpha = 1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(fadeOutProgress)
+ timings,
+ linearProgress,
+ timings.contentAfterFadeInDelay,
+ timings.contentAfterFadeInDuration
+ )
+ val alpha =
+ 1 - interpolators.contentAfterFadeInInterpolator.getInterpolation(fadeOutProgress)
drawable.alpha = (alpha * 0xFF).roundToInt()
if (drawHole) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 1d92170..f2d926d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -25,7 +25,6 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
-import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
@@ -301,24 +300,15 @@
*/
@Provides
@SysUISingleton
- static LaunchAnimator provideLaunchAnimator(Context context) {
- return new LaunchAnimator(context);
+ static ActivityLaunchAnimator provideActivityLaunchAnimator() {
+ return new ActivityLaunchAnimator();
}
/**
*/
@Provides
@SysUISingleton
- static ActivityLaunchAnimator provideActivityLaunchAnimator(LaunchAnimator launchAnimator) {
- return new ActivityLaunchAnimator(launchAnimator);
- }
-
- /**
- */
- @Provides
- @SysUISingleton
- static DialogLaunchAnimator provideDialogLaunchAnimator(Context context,
- LaunchAnimator launchAnimator, IDreamManager dreamManager) {
- return new DialogLaunchAnimator(context, launchAnimator, dreamManager);
+ static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager) {
+ return new DialogLaunchAnimator(dreamManager);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
index 64a7305..349b191 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
@@ -2,6 +2,7 @@
import android.util.MathUtils
import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.LaunchAnimator
import kotlin.math.min
@@ -55,6 +56,7 @@
}
fun getProgress(delay: Long, duration: Long): Float {
- return LaunchAnimator.getProgress(linearProgress, delay, duration)
+ return LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS, linearProgress, delay,
+ duration)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 20a771f..261b5db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -110,6 +110,7 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.biometrics.AuthController;
@@ -225,7 +226,8 @@
*/
private static final int FLING_HIDE = 2;
private static final long ANIMATION_DELAY_ICON_FADE_IN =
- LaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION
+ ActivityLaunchAnimator.TIMINGS.getTotalDuration()
+ - CollapsedStatusBarFragment.FADE_IN_DURATION
- CollapsedStatusBarFragment.FADE_IN_DELAY - 48;
private final DozeParameters mDozeParameters;
@@ -3629,8 +3631,8 @@
}
public void applyLaunchAnimationProgress(float linearProgress) {
- boolean hideIcons = LaunchAnimator.getProgress(linearProgress,
- ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
+ boolean hideIcons = LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS,
+ linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
if (hideIcons != mHideIconsDuringLaunchAnimation) {
mHideIconsDuringLaunchAnimation = hideIcons;
if (!hideIcons) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index 32aae6c..2ba37c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -23,7 +23,8 @@
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
statusBar.notificationPanelViewController.setIsLaunchAnimationRunning(true)
if (!isExpandingFullyAbove) {
- statusBar.collapsePanelWithDuration(LaunchAnimator.ANIMATION_DURATION.toInt())
+ statusBar.collapsePanelWithDuration(
+ ActivityLaunchAnimator.TIMINGS.totalDuration.toInt())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index d819fa2..1fe3d44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -46,7 +46,7 @@
@RunWithLooper
class ActivityLaunchAnimatorTest : SysuiTestCase() {
private val launchContainer = LinearLayout(mContext)
- private val launchAnimator = LaunchAnimator(mContext, isForTesting = true)
+ private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
@Mock lateinit var callback: ActivityLaunchAnimator.Callback
@Spy private val controller = TestLaunchAnimatorController(launchContainer)
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index f9ad740..b951345 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -33,7 +33,7 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class DialogLaunchAnimatorTest : SysuiTestCase() {
- private val launchAnimator = LaunchAnimator(context, isForTesting = true)
+ private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
private val attachedViews = mutableSetOf<View>()
@@ -42,7 +42,8 @@
@Before
fun setUp() {
- dialogLaunchAnimator = DialogLaunchAnimator(context, launchAnimator, dreamManager)
+ dialogLaunchAnimator = DialogLaunchAnimator(
+ dreamManager, launchAnimator, isForTesting = true)
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
new file mode 100644
index 0000000..dadf94e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
@@ -0,0 +1,23 @@
+package com.android.systemui.animation
+
+/**
+ * A [LaunchAnimator.Timings] to be used in tests.
+ *
+ * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
+ * when computing the progress of a sub-animation (the contents fade in/out).
+ */
+val TEST_TIMINGS = LaunchAnimator.Timings(
+ totalDuration = 0L,
+ contentBeforeFadeOutDelay = 1L,
+ contentBeforeFadeOutDuration = 1L,
+ contentAfterFadeInDelay = 1L,
+ contentAfterFadeInDuration = 1L
+)
+
+/** A [LaunchAnimator.Interpolators] to be used in tests. */
+val TEST_INTERPOLATORS = LaunchAnimator.Interpolators(
+ positionInterpolator = Interpolators.STANDARD,
+ positionXInterpolator = Interpolators.STANDARD,
+ contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
+ contentAfterFadeInInterpolator = Interpolators.STANDARD
+)
\ No newline at end of file