diff options
8 files changed, 102 insertions, 17 deletions
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp index 9248be9220af..ab9ab831157c 100644 --- a/packages/SystemUI/animation/Android.bp +++ b/packages/SystemUI/animation/Android.bp @@ -21,7 +21,7 @@ package { default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], } -java_library { +android_library { name: "SystemUIAnimationLib", @@ -30,9 +30,15 @@ java_library { "src/**/*.kt", ], + resource_dirs: [ + "res", + ], + static_libs: [ "PluginCoreLib", "SystemUI-sensors", ], + manifest: "AndroidManifest.xml", + } diff --git a/packages/SystemUI/animation/AndroidManifest.xml b/packages/SystemUI/animation/AndroidManifest.xml new file mode 100644 index 000000000000..321cc53142c6 --- /dev/null +++ b/packages/SystemUI/animation/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2017 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.systemui.animation"> + + +</manifest> diff --git a/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_x.xml b/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_x.xml new file mode 100644 index 000000000000..620dd4891b9b --- /dev/null +++ b/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_x.xml @@ -0,0 +1,18 @@ +<?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 new file mode 100644 index 000000000000..a268abce0c27 --- /dev/null +++ b/packages/SystemUI/animation/res/interpolator/launch_animation_interpolator_y.xml @@ -0,0 +1,18 @@ +<?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 87f52d564782..fc03a9ee9c5f 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -5,6 +5,7 @@ import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.app.ActivityManager import android.app.PendingIntent +import android.content.Context import android.graphics.Matrix import android.graphics.Rect import android.os.RemoteException @@ -16,6 +17,7 @@ import android.view.RemoteAnimationTarget import android.view.SyncRtSurfaceTransactionApplier import android.view.View import android.view.WindowManager +import android.view.animation.AnimationUtils import android.view.animation.PathInterpolator import com.android.internal.annotations.VisibleForTesting import com.android.internal.policy.ScreenDecorationsUtils @@ -25,11 +27,12 @@ import kotlin.math.roundToInt * 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 { +class ActivityLaunchAnimator(context: Context) { companion object { - const val ANIMATION_DURATION = 400L - const val ANIMATION_DURATION_FADE_OUT_CONTENT = 67L - const val ANIMATION_DURATION_FADE_IN_WINDOW = 200L + const val ANIMATION_DURATION = 500L + const val ANIMATION_DURATION_FADE_OUT_CONTENT = 183L + const val ANIMATION_DURATION_FADE_IN_WINDOW = 216L + const val ANIMATION_DELAY_FADE_IN_WINDOW = 166L 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 = @@ -53,6 +56,14 @@ class ActivityLaunchAnimator { } } + /** 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) + /** * Start an intent and animate the opening window. The intent will be started by running * [intentStarter], which should use the provided [RemoteAnimationAdapter] and return the launch @@ -104,6 +115,10 @@ class ActivityLaunchAnimator { startIntentWithAnimation(controller) { intentStarter.startPendingIntent(it) } } + /** Create a new animation [Runner] controlled by [controller]. */ + @VisibleForTesting + fun createRunner(controller: Controller): Runner = Runner(controller) + interface PendingIntentStarter { /** * Start a pending intent using the provided [animationAdapter] and return the launch @@ -228,7 +243,7 @@ class ActivityLaunchAnimator { } @VisibleForTesting - class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() { + inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() { private val rootView = controller.getRootView() @PublishedApi internal val context = rootView.context private val transactionApplier = SyncRtSurfaceTransactionApplier(rootView) @@ -308,6 +323,8 @@ class ActivityLaunchAnimator { val startBottom = state.bottom val startLeft = state.left val startRight = state.right + val startXCenter = (startLeft + startRight) / 2f + val startWidth = startRight - startLeft val startTopCornerRadius = state.topCornerRadius val startBottomCornerRadius = state.bottomCornerRadius @@ -318,6 +335,8 @@ class ActivityLaunchAnimator { val endBottom = windowBounds.bottom val endLeft = windowBounds.left val endRight = windowBounds.right + val endXCenter = (endLeft + endRight) / 2f + val endWidth = endRight - endLeft // TODO(b/184121838): Ensure that we are launching on the same screen. val rootViewLocation = rootView.locationOnScreen @@ -359,14 +378,16 @@ class ActivityLaunchAnimator { return@addUpdateListener } - // TODO(b/184121838): Use android.R.interpolator.fast_out_extra_slow_in instead. val linearProgress = animation.animatedFraction - val progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(linearProgress) + val progress = animationInterpolator.getInterpolation(linearProgress) + val xProgress = animationInterpolatorX.getInterpolation(linearProgress) + val xCenter = MathUtils.lerp(startXCenter, endXCenter, xProgress) + val halfWidth = lerp(startWidth, endWidth, progress) / 2 state.top = lerp(startTop, endTop, progress).roundToInt() state.bottom = lerp(startBottom, endBottom, progress).roundToInt() - state.left = lerp(startLeft, endLeft, progress).roundToInt() - state.right = lerp(startRight, endRight, progress).roundToInt() + state.left = (xCenter - halfWidth).roundToInt() + state.right = (xCenter + halfWidth).roundToInt() state.topCornerRadius = MathUtils.lerp(startTopCornerRadius, endRadius, progress) state.bottomCornerRadius = @@ -378,7 +399,7 @@ class ActivityLaunchAnimator { 1 - Interpolators.ALPHA_OUT.getInterpolation(contentAlphaProgress) val backgroundAlphaProgress = getProgress(linearProgress, - ANIMATION_DURATION_FADE_OUT_CONTENT, ANIMATION_DURATION_FADE_IN_WINDOW) + ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW) state.backgroundAlpha = 1 - Interpolators.ALPHA_IN.getInterpolation(backgroundAlphaProgress) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java index 288878b9e32c..af8b4d99792a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java @@ -284,7 +284,7 @@ public class NotificationBackgroundView extends View { mActualWidth = params.getWidth(); float alphaProgress = Interpolators.ALPHA_IN.getInterpolation( params.getProgress( - ActivityLaunchAnimator.ANIMATION_DURATION_FADE_OUT_CONTENT /* delay */, + ActivityLaunchAnimator.ANIMATION_DELAY_FADE_IN_WINDOW /* delay */, ActivityLaunchAnimator.ANIMATION_DURATION_FADE_IN_WINDOW /* duration */)); mBackground.setAlpha((int) (mDrawableAlpha * (1.0f - alphaProgress))); invalidate(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 297348613ac0..630f753de84d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1374,7 +1374,7 @@ public class StatusBar extends SystemUI implements DemoMode, private void setUpPresenter() { // Set up the initial notification state. - mActivityLaunchAnimator = new ActivityLaunchAnimator(); + mActivityLaunchAnimator = new ActivityLaunchAnimator(mContext); mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( mNotificationShadeWindowViewController, mNotificationPanelViewController, 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 6f41cb0cd510..6420c4e2c481 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt @@ -35,7 +35,7 @@ import kotlin.concurrent.thread @RunWith(AndroidTestingRunner::class) @RunWithLooper class ActivityLaunchAnimatorTest : SysuiTestCase() { - private val activityLaunchAnimator = ActivityLaunchAnimator() + private val activityLaunchAnimator = ActivityLaunchAnimator(mContext) private val rootView = View(mContext) @Spy private val controller = TestLaunchAnimatorController(rootView) @Mock lateinit var iCallback: IRemoteAnimationFinishedCallback @@ -96,7 +96,7 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { @Test fun doesNotStartIfAnimationIsCancelled() { - val runner = ActivityLaunchAnimator.Runner(controller) + val runner = activityLaunchAnimator.createRunner(controller) runner.onAnimationCancelled() runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback) @@ -107,7 +107,7 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { @Test fun abortsIfNoOpeningWindowIsFound() { - val runner = ActivityLaunchAnimator.Runner(controller) + val runner = activityLaunchAnimator.createRunner(controller) runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback) waitForIdleSync() @@ -117,7 +117,7 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { @Test fun startsAnimationIfWindowIsOpening() { - val runner = ActivityLaunchAnimator.Runner(controller) + val runner = activityLaunchAnimator.createRunner(controller) runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback) waitForIdleSync() verify(controller).onLaunchAnimationStart(anyBoolean()) |