1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM; 20 21 import android.annotation.NonNull; 22 import android.view.SurfaceControl; 23 import android.view.animation.AlphaAnimation; 24 import android.view.animation.Animation; 25 import android.view.animation.Interpolator; 26 import android.view.animation.PathInterpolator; 27 28 /** 29 * Controller to fade in and out navigation bar during app transition when 30 * config_attachNavBarToAppDuringTransition is true. 31 */ 32 public class NavBarFadeAnimationController extends FadeAnimationController{ 33 private static final int FADE_IN_DURATION = 266; 34 private static final int FADE_OUT_DURATION = 133; 35 private static final Interpolator FADE_IN_INTERPOLATOR = 36 new PathInterpolator(0f, 0f, 0f, 1f); 37 private static final Interpolator FADE_OUT_INTERPOLATOR = 38 new PathInterpolator(0.2f, 0f, 1f, 1f); 39 40 private final WindowState mNavigationBar; 41 private Animation mFadeInAnimation; 42 private Animation mFadeOutAnimation; 43 private SurfaceControl mFadeInParent; 44 private SurfaceControl mFadeOutParent; 45 private boolean mPlaySequentially = false; 46 NavBarFadeAnimationController(DisplayContent displayContent)47 public NavBarFadeAnimationController(DisplayContent displayContent) { 48 super(displayContent); 49 mNavigationBar = displayContent.getDisplayPolicy().getNavigationBar(); 50 mFadeInAnimation = new AlphaAnimation(0f, 1f); 51 mFadeInAnimation.setDuration(FADE_IN_DURATION); 52 mFadeInAnimation.setInterpolator(FADE_IN_INTERPOLATOR); 53 54 mFadeOutAnimation = new AlphaAnimation(1f, 0f); 55 mFadeOutAnimation.setDuration(FADE_OUT_DURATION); 56 mFadeOutAnimation.setInterpolator(FADE_OUT_INTERPOLATOR); 57 } 58 59 @Override getFadeInAnimation()60 public Animation getFadeInAnimation() { 61 return mFadeInAnimation; 62 } 63 64 @Override getFadeOutAnimation()65 public Animation getFadeOutAnimation() { 66 return mFadeOutAnimation; 67 } 68 69 @Override createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec, boolean show, WindowToken windowToken)70 protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec, 71 boolean show, WindowToken windowToken) { 72 return new NavFadeAnimationAdapter( 73 animationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken, 74 show ? mFadeInParent : mFadeOutParent); 75 } 76 77 /** 78 * Run the fade-in/out animation for the navigation bar. 79 * 80 * @param show true for fade-in, otherwise for fade-out. 81 */ fadeWindowToken(boolean show)82 public void fadeWindowToken(boolean show) { 83 final AsyncRotationController controller = 84 mDisplayContent.getAsyncRotationController(); 85 final Runnable fadeAnim = () -> fadeWindowToken(show, mNavigationBar.mToken, 86 ANIMATION_TYPE_TOKEN_TRANSFORM); 87 if (controller == null) { 88 fadeAnim.run(); 89 } else if (!controller.hasFadeOperation(mNavigationBar.mToken)) { 90 // If fade rotation animation is running and the nav bar is not controlled by it: 91 // - For fade-in animation, defer the animation until fade rotation animation finishes. 92 // - For fade-out animation, just play the animation. 93 if (show) { 94 controller.setOnShowRunnable(fadeAnim); 95 } else { 96 fadeAnim.run(); 97 } 98 } 99 } 100 fadeOutAndInSequentially(long totalDuration, SurfaceControl fadeOutParent, SurfaceControl fadeInParent)101 void fadeOutAndInSequentially(long totalDuration, SurfaceControl fadeOutParent, 102 SurfaceControl fadeInParent) { 103 mPlaySequentially = true; 104 if (totalDuration > 0) { 105 // The animation duration of each animation varies so we set the fade-out duration to 106 // 1/3 of the total app transition duration and set the fade-in duration to 2/3 of it. 107 final long fadeInDuration = totalDuration * 2L / 3L; 108 mFadeOutAnimation.setDuration(totalDuration - fadeInDuration); 109 mFadeInAnimation.setDuration(fadeInDuration); 110 } 111 mFadeOutParent = fadeOutParent; 112 mFadeInParent = fadeInParent; 113 fadeWindowToken(false); 114 } 115 116 /** 117 * The animation adapter that is capable of playing fade-out and fade-in sequentially and 118 * reparenting the navigation bar to a specified SurfaceControl when fade animation starts. 119 */ 120 protected class NavFadeAnimationAdapter extends FadeAnimationAdapter { 121 private SurfaceControl mParent; 122 NavFadeAnimationAdapter(AnimationSpec windowAnimationSpec, SurfaceAnimationRunner surfaceAnimationRunner, boolean show, WindowToken token, SurfaceControl parent)123 NavFadeAnimationAdapter(AnimationSpec windowAnimationSpec, 124 SurfaceAnimationRunner surfaceAnimationRunner, boolean show, 125 WindowToken token, SurfaceControl parent) { 126 super(windowAnimationSpec, surfaceAnimationRunner, show, token); 127 mParent = parent; 128 } 129 130 @Override startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, int type, @NonNull SurfaceAnimator.OnAnimationFinishedCallback finishCallback)131 public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, 132 int type, @NonNull SurfaceAnimator.OnAnimationFinishedCallback finishCallback) { 133 super.startAnimation(animationLeash, t, type, finishCallback); 134 if (mParent != null && mParent.isValid()) { 135 t.reparent(animationLeash, mParent); 136 // Place the nav bar on top of anything else (e.g. ime and starting window) in the 137 // parent. 138 t.setLayer(animationLeash, Integer.MAX_VALUE); 139 } 140 } 141 142 @Override shouldDeferAnimationFinish(Runnable endDeferFinishCallback)143 public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { 144 if (mPlaySequentially) { 145 if (!mShow) { 146 fadeWindowToken(true); 147 } 148 return false; 149 } else { 150 return super.shouldDeferAnimationFinish(endDeferFinishCallback); 151 } 152 } 153 } 154 } 155