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.AnimationSpecProto.WINDOW;
20 import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
21 
22 import android.annotation.NonNull;
23 import android.content.Context;
24 import android.util.ArrayMap;
25 import android.util.proto.ProtoOutputStream;
26 import android.view.SurfaceControl;
27 import android.view.animation.Animation;
28 import android.view.animation.AnimationUtils;
29 import android.view.animation.Transformation;
30 
31 import com.android.internal.R;
32 
33 import java.io.PrintWriter;
34 
35 /**
36  * An animation controller to fade-in/out for a window token.
37  */
38 public class FadeAnimationController {
39     protected final Context mContext;
40     protected final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();
41 
FadeAnimationController(DisplayContent displayContent)42     public FadeAnimationController(DisplayContent displayContent) {
43         mContext = displayContent.mWmService.mContext;
44     }
45 
46     /**
47      * @return a fade-in Animation.
48      */
getFadeInAnimation()49     public Animation getFadeInAnimation() {
50         return AnimationUtils.loadAnimation(mContext, R.anim.fade_in);
51     }
52 
53     /**
54      * @return a fade-out Animation.
55      */
getFadeOutAnimation()56     public Animation getFadeOutAnimation() {
57         return AnimationUtils.loadAnimation(mContext, R.anim.fade_out);
58     }
59 
60     /**
61      * Run the fade in/out animation for a window token.
62      *
63      * @param show true for fade-in, otherwise for fade-out.
64      * @param windowToken the window token to run the animation.
65      * @param animationType the animation type defined in SurfaceAnimator.
66      */
fadeWindowToken(boolean show, WindowToken windowToken, int animationType)67     public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
68         if (windowToken == null || windowToken.getParent() == null) {
69             return;
70         }
71 
72         final FadeAnimationAdapter animationAdapter = createAdapter(show, windowToken);
73         if (animationAdapter == null) {
74             return;
75         }
76 
77         // We deferred the end of the animation when hiding the token, so we need to end it now that
78         // it's shown again.
79         final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> {
80             final Runnable runnable = mDeferredFinishCallbacks.remove(windowToken);
81             if (runnable != null) {
82                 runnable.run();
83             }
84         } : null;
85         windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter,
86                 show /* hidden */, animationType, finishedCallback);
87     }
88 
createAdapter(boolean show, WindowToken windowToken)89     protected FadeAnimationAdapter createAdapter(boolean show, WindowToken windowToken) {
90         final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
91         if (animation == null) {
92             return null;
93         }
94 
95         final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
96                 createAnimationSpec(animation);
97 
98         return new FadeAnimationAdapter(
99                 windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken);
100     }
101 
createAnimationSpec( @onNull Animation animation)102     protected LocalAnimationAdapter.AnimationSpec createAnimationSpec(
103             @NonNull Animation animation) {
104         return new LocalAnimationAdapter.AnimationSpec() {
105 
106             final Transformation mTransformation = new Transformation();
107 
108             @Override
109             public boolean getShowWallpaper() {
110                 return true;
111             }
112 
113             @Override
114             public long getDuration() {
115                 return animation.getDuration();
116             }
117 
118             @Override
119             public void apply(SurfaceControl.Transaction t, SurfaceControl leash,
120                     long currentPlayTime) {
121                 mTransformation.clear();
122                 animation.getTransformation(currentPlayTime, mTransformation);
123                 t.setAlpha(leash, mTransformation.getAlpha());
124             }
125 
126             @Override
127             public void dump(PrintWriter pw, String prefix) {
128                 pw.print(prefix);
129                 pw.println(animation);
130             }
131 
132             @Override
133             public void dumpDebugInner(ProtoOutputStream proto) {
134                 final long token = proto.start(WINDOW);
135                 proto.write(ANIMATION, animation.toString());
136                 proto.end(token);
137             }
138         };
139     }
140 
141     protected class FadeAnimationAdapter extends LocalAnimationAdapter {
142         protected final boolean mShow;
143         private final WindowToken mToken;
144 
FadeAnimationAdapter(AnimationSpec windowAnimationSpec, SurfaceAnimationRunner surfaceAnimationRunner, boolean show, WindowToken token)145         FadeAnimationAdapter(AnimationSpec windowAnimationSpec,
146                 SurfaceAnimationRunner surfaceAnimationRunner, boolean show,
147                 WindowToken token) {
148             super(windowAnimationSpec, surfaceAnimationRunner);
149             mShow = show;
150             mToken = token;
151         }
152 
153         @Override
shouldDeferAnimationFinish(Runnable endDeferFinishCallback)154         public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
155             // We defer the end of the hide animation to ensure the tokens stay hidden until
156             // we show them again.
157             if (!mShow) {
158                 mDeferredFinishCallbacks.put(mToken, endDeferFinishCallback);
159                 return true;
160             }
161             return false;
162         }
163     }
164 }
165