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