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 android.view;
18 
19 import static android.view.InsetsAnimationControlImplProto.CURRENT_ALPHA;
20 import static android.view.InsetsAnimationControlImplProto.IS_CANCELLED;
21 import static android.view.InsetsAnimationControlImplProto.IS_FINISHED;
22 import static android.view.InsetsAnimationControlImplProto.PENDING_ALPHA;
23 import static android.view.InsetsAnimationControlImplProto.PENDING_FRACTION;
24 import static android.view.InsetsAnimationControlImplProto.PENDING_INSETS;
25 import static android.view.InsetsAnimationControlImplProto.SHOWN_ON_FINISH;
26 import static android.view.InsetsAnimationControlImplProto.TMP_MATRIX;
27 import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
28 
29 import android.animation.Animator;
30 import android.animation.AnimatorListenerAdapter;
31 import android.animation.ValueAnimator;
32 import android.graphics.Insets;
33 import android.graphics.Rect;
34 import android.util.SparseArray;
35 import android.util.proto.ProtoOutputStream;
36 import android.view.WindowInsets.Type.InsetsType;
37 import android.view.WindowInsetsAnimation.Bounds;
38 import android.view.animation.Interpolator;
39 import android.view.inputmethod.ImeTracker;
40 
41 /**
42  * Runs a fake animation of resizing insets to produce insets animation callbacks.
43  * @hide
44  */
45 public class InsetsResizeAnimationRunner implements InsetsAnimationControlRunner,
46         InternalInsetsAnimationController, WindowInsetsAnimationControlListener {
47 
48     private final InsetsState mFromState;
49     private final InsetsState mToState;
50     private final @InsetsType int mTypes;
51     private final WindowInsetsAnimation mAnimation;
52     private final InsetsAnimationControlCallbacks mController;
53     private ValueAnimator mAnimator;
54     private boolean mCancelled;
55     private boolean mFinished;
56 
InsetsResizeAnimationRunner(Rect frame, InsetsState fromState, InsetsState toState, Interpolator interpolator, long duration, @InsetsType int types, InsetsAnimationControlCallbacks controller)57     public InsetsResizeAnimationRunner(Rect frame, InsetsState fromState, InsetsState toState,
58             Interpolator interpolator, long duration, @InsetsType int types,
59             InsetsAnimationControlCallbacks controller) {
60         mFromState = fromState;
61         mToState = toState;
62         mTypes = types;
63         mController = controller;
64         mAnimation = new WindowInsetsAnimation(types, interpolator, duration);
65         mAnimation.setAlpha(1f);
66         final Insets fromInsets = fromState.calculateInsets(
67                 frame, types, false /* ignoreVisibility */);
68         final Insets toInsets = toState.calculateInsets(
69                 frame, types, false /* ignoreVisibility */);
70         controller.startAnimation(this, this, types, mAnimation,
71                 new Bounds(Insets.min(fromInsets, toInsets), Insets.max(fromInsets, toInsets)));
72     }
73 
74     @Override
getTypes()75     public int getTypes() {
76         return mTypes;
77     }
78 
79     @Override
getControllingTypes()80     public int getControllingTypes() {
81         return mTypes;
82     }
83 
84     @Override
getAnimation()85     public WindowInsetsAnimation getAnimation() {
86         return mAnimation;
87     }
88 
89     @Override
getAnimationType()90     public int getAnimationType() {
91         return ANIMATION_TYPE_RESIZE;
92     }
93 
94     @Override
getStatsToken()95     public ImeTracker.Token getStatsToken() {
96         // Return null as resizing the IME view is not explicitly tracked.
97         return null;
98     }
99 
100     @Override
cancel()101     public void cancel() {
102         if (mCancelled || mFinished) {
103             return;
104         }
105         mCancelled = true;
106         if (mAnimator != null) {
107             mAnimator.cancel();
108         }
109     }
110 
111     @Override
isCancelled()112     public boolean isCancelled() {
113         return mCancelled;
114     }
115 
116     @Override
onReady(WindowInsetsAnimationController controller, int types)117     public void onReady(WindowInsetsAnimationController controller, int types) {
118         if (mCancelled) {
119             return;
120         }
121         mAnimator = ValueAnimator.ofFloat(0f, 1f);
122         mAnimator.setDuration(mAnimation.getDurationMillis());
123         mAnimator.addUpdateListener(animation -> {
124             mAnimation.setFraction(animation.getAnimatedFraction());
125             mController.scheduleApplyChangeInsets(InsetsResizeAnimationRunner.this);
126         });
127         mAnimator.addListener(new AnimatorListenerAdapter() {
128 
129             @Override
130             public void onAnimationEnd(Animator animation) {
131                 mFinished = true;
132                 mController.scheduleApplyChangeInsets(InsetsResizeAnimationRunner.this);
133             }
134         });
135         mAnimator.start();
136     }
137 
138     @Override
applyChangeInsets(InsetsState outState)139     public boolean applyChangeInsets(InsetsState outState) {
140         if (mCancelled) {
141             return false;
142         }
143         final float fraction = mAnimation.getInterpolatedFraction();
144         InsetsState.traverse(mFromState, mToState, new InsetsState.OnTraverseCallbacks() {
145             @Override
146             public void onIdMatch(InsetsSource fromSource, InsetsSource toSource) {
147                 final Rect fromFrame = fromSource.getFrame();
148                 final Rect toFrame = toSource.getFrame();
149                 final Rect frame = new Rect(
150                         (int) (fromFrame.left + fraction * (toFrame.left - fromFrame.left)),
151                         (int) (fromFrame.top + fraction * (toFrame.top - fromFrame.top)),
152                         (int) (fromFrame.right + fraction * (toFrame.right - fromFrame.right)),
153                         (int) (fromFrame.bottom + fraction * (toFrame.bottom - fromFrame.bottom)));
154                 final InsetsSource source =
155                         new InsetsSource(fromSource.getId(), fromSource.getType());
156                 source.setFrame(frame);
157                 source.setVisible(toSource.isVisible());
158                 outState.addSource(source);
159             }
160         });
161         if (mFinished) {
162             mController.notifyFinished(this, true /* shown */);
163         }
164         return mFinished;
165     }
166 
167     @Override
dumpDebug(ProtoOutputStream proto, long fieldId)168     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
169         final long token = proto.start(fieldId);
170         proto.write(IS_CANCELLED, mCancelled);
171         proto.write(IS_FINISHED, mFinished);
172         proto.write(TMP_MATRIX, "null");
173         proto.write(PENDING_INSETS, "null");
174         proto.write(PENDING_FRACTION, mAnimation.getInterpolatedFraction());
175         proto.write(SHOWN_ON_FINISH, true);
176         proto.write(CURRENT_ALPHA, 1f);
177         proto.write(PENDING_ALPHA, 1f);
178         proto.end(token);
179     }
180 
181     @Override
getHiddenStateInsets()182     public Insets getHiddenStateInsets() {
183         return Insets.NONE;
184     }
185 
186     @Override
getShownStateInsets()187     public Insets getShownStateInsets() {
188         return Insets.NONE;
189     }
190 
191     @Override
getCurrentInsets()192     public Insets getCurrentInsets() {
193         return Insets.NONE;
194     }
195 
196     @Override
getCurrentFraction()197     public float getCurrentFraction() {
198         return 0;
199     }
200 
201     @Override
getCurrentAlpha()202     public float getCurrentAlpha() {
203         return 0;
204     }
205 
206     @Override
setInsetsAndAlpha(Insets insets, float alpha, float fraction)207     public void setInsetsAndAlpha(Insets insets, float alpha, float fraction) {
208     }
209 
210     @Override
finish(boolean shown)211     public void finish(boolean shown) {
212     }
213 
214     @Override
isFinished()215     public boolean isFinished() {
216         return false;
217     }
218 
219     @Override
notifyControlRevoked(int types)220     public void notifyControlRevoked(int types) {
221     }
222 
223     @Override
updateSurfacePosition(SparseArray<InsetsSourceControl> controls)224     public void updateSurfacePosition(SparseArray<InsetsSourceControl> controls) {
225     }
226 
227     @Override
hasZeroInsetsIme()228     public boolean hasZeroInsetsIme() {
229         return false;
230     }
231 
232     @Override
setReadyDispatched(boolean dispatched)233     public void setReadyDispatched(boolean dispatched) {
234     }
235 
236     @Override
onFinished(WindowInsetsAnimationController controller)237     public void onFinished(WindowInsetsAnimationController controller) {
238     }
239 
240     @Override
onCancelled(WindowInsetsAnimationController controller)241     public void onCancelled(WindowInsetsAnimationController controller) {
242     }
243 }
244