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