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