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