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_APP_TRANSITION; 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 DisplayContent mDisplayContent; 41 private final WindowState mNavigationBar; 42 private Animation mFadeInAnimation; 43 private Animation mFadeOutAnimation; 44 private SurfaceControl mFadeInParent; 45 private SurfaceControl mFadeOutParent; 46 private boolean mPlaySequentially = false; 47 NavBarFadeAnimationController(DisplayContent displayContent)48 public NavBarFadeAnimationController(DisplayContent displayContent) { 49 super(displayContent); 50 mDisplayContent = displayContent; 51 mNavigationBar = displayContent.getDisplayPolicy().getNavigationBar(); 52 mFadeInAnimation = new AlphaAnimation(0f, 1f); 53 mFadeInAnimation.setDuration(FADE_IN_DURATION); 54 mFadeInAnimation.setInterpolator(FADE_IN_INTERPOLATOR); 55 56 mFadeOutAnimation = new AlphaAnimation(1f, 0f); 57 mFadeOutAnimation.setDuration(FADE_OUT_DURATION); 58 mFadeOutAnimation.setInterpolator(FADE_OUT_INTERPOLATOR); 59 } 60 61 @Override getFadeInAnimation()62 public Animation getFadeInAnimation() { 63 return mFadeInAnimation; 64 } 65 66 @Override getFadeOutAnimation()67 public Animation getFadeOutAnimation() { 68 return mFadeOutAnimation; 69 } 70 71 @Override createAdapter(boolean show, WindowToken windowToken)72 protected FadeAnimationAdapter createAdapter(boolean show, WindowToken windowToken) { 73 final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation(); 74 if (animation == null) { 75 return null; 76 } 77 78 final LocalAnimationAdapter.AnimationSpec windowAnimationSpec = 79 createAnimationSpec(animation); 80 return new NavFadeAnimationAdapter( 81 windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken, 82 show ? mFadeInParent : mFadeOutParent); 83 } 84 85 /** 86 * Run the fade-in/out animation for the navigation bar. 87 * 88 * @param show true for fade-in, otherwise for fade-out. 89 */ fadeWindowToken(boolean show)90 public void fadeWindowToken(boolean show) { 91 final FadeRotationAnimationController controller = 92 mDisplayContent.getFadeRotationAnimationController(); 93 final Runnable fadeAnim = () -> fadeWindowToken(show, mNavigationBar.mToken, 94 ANIMATION_TYPE_APP_TRANSITION); 95 if (controller == null) { 96 fadeAnim.run(); 97 } else if (!controller.isTargetToken(mNavigationBar.mToken)) { 98 // If fade rotation animation is running and the nav bar is not controlled by it: 99 // - For fade-in animation, defer the animation until fade rotation animation finishes. 100 // - For fade-out animation, just play the animation. 101 if (show) { 102 controller.setOnShowRunnable(fadeAnim); 103 } else { 104 fadeAnim.run(); 105 } 106 } else { 107 // If fade rotation animation is running and controlling the nav bar, make sure we empty 108 // the mDeferredFinishCallbacks and defer the runnable until fade rotation animation 109 // finishes. 110 final Runnable runnable = mDeferredFinishCallbacks.remove(mNavigationBar.mToken); 111 if (runnable != null) { 112 controller.setOnShowRunnable(runnable); 113 } 114 } 115 } 116 fadeOutAndInSequentially(long totalDuration, SurfaceControl fadeOutParent, SurfaceControl fadeInParent)117 void fadeOutAndInSequentially(long totalDuration, SurfaceControl fadeOutParent, 118 SurfaceControl fadeInParent) { 119 mPlaySequentially = true; 120 if (totalDuration > 0) { 121 // The animation duration of each animation varies so we set the fade-out duration to 122 // 1/3 of the total app transition duration and set the fade-in duration to 2/3 of it. 123 final long fadeInDuration = totalDuration * 2L / 3L; 124 mFadeOutAnimation.setDuration(totalDuration - fadeInDuration); 125 mFadeInAnimation.setDuration(fadeInDuration); 126 } 127 mFadeOutParent = fadeOutParent; 128 mFadeInParent = fadeInParent; 129 fadeWindowToken(false); 130 } 131 132 /** 133 * The animation adapter that is capable of playing fade-out and fade-in sequentially and 134 * reparenting the navigation bar to a specified SurfaceControl when fade animation starts. 135 */ 136 protected class NavFadeAnimationAdapter extends FadeAnimationAdapter { 137 private SurfaceControl mParent; 138 NavFadeAnimationAdapter(AnimationSpec windowAnimationSpec, SurfaceAnimationRunner surfaceAnimationRunner, boolean show, WindowToken token, SurfaceControl parent)139 NavFadeAnimationAdapter(AnimationSpec windowAnimationSpec, 140 SurfaceAnimationRunner surfaceAnimationRunner, boolean show, 141 WindowToken token, SurfaceControl parent) { 142 super(windowAnimationSpec, surfaceAnimationRunner, show, token); 143 mParent = parent; 144 } 145 146 @Override startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, int type, @NonNull SurfaceAnimator.OnAnimationFinishedCallback finishCallback)147 public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, 148 int type, @NonNull SurfaceAnimator.OnAnimationFinishedCallback finishCallback) { 149 super.startAnimation(animationLeash, t, type, finishCallback); 150 if (mParent != null && mParent.isValid()) { 151 t.reparent(animationLeash, mParent); 152 // Place the nav bar on top of anything else (e.g. ime and starting window) in the 153 // parent. 154 t.setLayer(animationLeash, Integer.MAX_VALUE); 155 } 156 } 157 158 @Override shouldDeferAnimationFinish(Runnable endDeferFinishCallback)159 public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { 160 if (mPlaySequentially) { 161 if (!mShow) { 162 fadeWindowToken(true); 163 } 164 return false; 165 } else { 166 return super.shouldDeferAnimationFinish(endDeferFinishCallback); 167 } 168 } 169 } 170 } 171