1 /*
2  * Copyright (C) 2020 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 android.annotation.FloatRange;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SuppressLint;
24 import android.graphics.Insets;
25 import android.view.animation.Interpolator;
26 
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 import java.util.List;
30 
31 /**
32  * Class representing an animation of a set of windows that cause insets.
33  */
34 public final class WindowInsetsAnimation {
35 
36     @WindowInsets.Type.InsetsType
37     private final int mTypeMask;
38     private float mFraction;
39     @Nullable
40     private final Interpolator mInterpolator;
41     private final long mDurationMillis;
42     private float mAlpha;
43 
44     /**
45      * Creates a new {@link WindowInsetsAnimation} object.
46      * <p>
47      * This should only be used for testing, as usually the system creates this object for the
48      * application to listen to with {@link Callback}.
49      * </p>
50      * @param typeMask The bitmask of {@link WindowInsets.Type}s that are animating.
51      * @param interpolator The interpolator of the animation.
52      * @param durationMillis The duration of the animation in
53      *                   {@link java.util.concurrent.TimeUnit#MILLISECONDS}.
54      */
WindowInsetsAnimation( @indowInsets.Type.InsetsType int typeMask, @Nullable Interpolator interpolator, long durationMillis)55     public WindowInsetsAnimation(
56             @WindowInsets.Type.InsetsType int typeMask, @Nullable Interpolator interpolator,
57             long durationMillis) {
58         mTypeMask = typeMask;
59         mInterpolator = interpolator;
60         mDurationMillis = durationMillis;
61     }
62 
63     /**
64      * @return The bitmask of {@link WindowInsets.Type.InsetsType}s that are animating.
65      */
66     @WindowInsets.Type.InsetsType
getTypeMask()67     public int getTypeMask() {
68         return mTypeMask;
69     }
70 
71     /**
72      * Returns the raw fractional progress of this animation between
73      * start state of the animation and the end state of the animation. Note
74      * that this progress is the global progress of the animation, whereas
75      * {@link Callback#onProgress} will only dispatch the insets that may
76      * be inset with {@link WindowInsets#inset} by parents of views in the hierarchy.
77      * Progress per insets animation is global for the entire animation. One animation animates
78      * all things together (in, out, ...). If they don't animate together, we'd have
79      * multiple animations.
80      * <p>
81      * Note: In case the application is controlling the animation, the valued returned here will
82      * be the same as the application passed into
83      * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
84      * </p>
85      * @return The current progress of this animation.
86      */
87     @FloatRange(from = 0f, to = 1f)
getFraction()88     public float getFraction() {
89         return mFraction;
90     }
91 
92     /**
93      * Returns the interpolated fractional progress of this animation between
94      * start state of the animation and the end state of the animation. Note
95      * that this progress is the global progress of the animation, whereas
96      * {@link Callback#onProgress} will only dispatch the insets that may
97      * be inset with {@link WindowInsets#inset} by parents of views in the hierarchy.
98      * Progress per insets animation is global for the entire animation. One animation animates
99      * all things together (in, out, ...). If they don't animate together, we'd have
100      * multiple animations.
101      * <p>
102      * Note: In case the application is controlling the animation, the valued returned here will
103      * be the same as the application passed into
104      * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)},
105      * interpolated with the interpolator passed into
106      * {@link WindowInsetsController#controlWindowInsetsAnimation}.
107      * </p>
108      * <p>
109      * Note: For system-initiated animations, this will always return a valid value between 0
110      * and 1.
111      * </p>
112      * @see #getFraction() for raw fraction.
113      * @return The current interpolated progress of this animation.
114      */
getInterpolatedFraction()115     public float getInterpolatedFraction() {
116         if (mInterpolator != null) {
117             return mInterpolator.getInterpolation(mFraction);
118         }
119         return mFraction;
120     }
121 
122     /**
123      * Retrieves the interpolator used for this animation, or {@code null} if this animation
124      * doesn't follow an interpolation curved. For system-initiated animations, this will never
125      * return {@code null}.
126      *
127      * @return The interpolator used for this animation.
128      */
129     @Nullable
getInterpolator()130     public Interpolator getInterpolator() {
131         return mInterpolator;
132     }
133 
134     /**
135      * @return duration of animation in {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or
136      *         -1 if the animation doesn't have a fixed duration.
137      */
getDurationMillis()138     public long getDurationMillis() {
139         return mDurationMillis;
140     }
141 
142     /**
143      * Set fraction of the progress if {@link WindowInsets.Type.InsetsType} animation is
144      * controlled by the app.
145      * <p>
146      * Note: This should only be used for testing, as the system fills in the fraction for the
147      * application or the fraction that was passed into
148      * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} is being
149      * used.
150      * </p>
151      * @param fraction fractional progress between 0 and 1 where 0 represents hidden and
152      *                zero progress and 1 represent fully shown final state.
153      * @see #getFraction()
154      */
setFraction(@loatRangefrom = 0f, to = 1f) float fraction)155     public void setFraction(@FloatRange(from = 0f, to = 1f) float fraction) {
156         mFraction = fraction;
157     }
158 
159     /**
160      * Retrieves the translucency of the windows that are animating.
161      *
162      * @return Alpha of windows that cause insets of type {@link WindowInsets.Type.InsetsType}.
163      */
164     @FloatRange(from = 0f, to = 1f)
getAlpha()165     public float getAlpha() {
166         return mAlpha;
167     }
168 
169     /**
170      * Sets the translucency of the windows that are animating.
171      * <p>
172      * Note: This should only be used for testing, as the system fills in the alpha for the
173      * application or the alpha that was passed into
174      * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} is being
175      * used.
176      * </p>
177      * @param alpha Alpha of windows that cause insets of type
178      *              {@link WindowInsets.Type.InsetsType}.
179      * @see #getAlpha()
180      */
setAlpha(@loatRangefrom = 0f, to = 1f) float alpha)181     public void setAlpha(@FloatRange(from = 0f, to = 1f) float alpha) {
182         mAlpha = alpha;
183     }
184 
185     /**
186      * Class representing the range of an {@link WindowInsetsAnimation}
187      */
188     public static final class Bounds {
189 
190         private final Insets mLowerBound;
191         private final Insets mUpperBound;
192 
Bounds(@onNull Insets lowerBound, @NonNull Insets upperBound)193         public Bounds(@NonNull Insets lowerBound, @NonNull Insets upperBound) {
194             mLowerBound = lowerBound;
195             mUpperBound = upperBound;
196         }
197 
198         /**
199          * Queries the lower inset bound of the animation. If the animation is about showing or
200          * hiding a window that cause insets, the lower bound is {@link Insets#NONE} and the upper
201          * bound is the same as {@link WindowInsets#getInsets(int)} for the fully shown state. This
202          * is the same as {@link WindowInsetsAnimationController#getHiddenStateInsets} and
203          * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets
204          * invoked because of an animation that originates from
205          * {@link WindowInsetsAnimationController}.
206          * <p>
207          * However, if the size of a window that causes insets is changing, these are the
208          * lower/upper bounds of that size animation.
209          * </p>
210          * There are no overlapping animations for a specific type, but there may be multiple
211          * animations running at the same time for different inset types.
212          *
213          * @see #getUpperBound()
214          * @see WindowInsetsAnimationController#getHiddenStateInsets
215          */
216         @NonNull
getLowerBound()217         public Insets getLowerBound() {
218             return mLowerBound;
219         }
220 
221         /**
222          * Queries the upper inset bound of the animation. If the animation is about showing or
223          * hiding a window that cause insets, the lower bound is {@link Insets#NONE}
224          * nd the upper bound is the same as {@link WindowInsets#getInsets(int)} for the fully
225          * shown state. This is the same as
226          * {@link WindowInsetsAnimationController#getHiddenStateInsets} and
227          * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets
228          * invoked because of an animation that originates from
229          * {@link WindowInsetsAnimationController}.
230          * <p>
231          * However, if the size of a window that causes insets is changing, these are the
232          * lower/upper bounds of that size animation.
233          * <p>
234          * There are no overlapping animations for a specific type, but there may be multiple
235          * animations running at the same time for different inset types.
236          *
237          * @see #getLowerBound()
238          * @see WindowInsetsAnimationController#getShownStateInsets
239          */
240         @NonNull
getUpperBound()241         public Insets getUpperBound() {
242             return mUpperBound;
243         }
244 
245         /**
246          * Insets both the lower and upper bound by the specified insets. This is to be used in
247          * {@link Callback#onStart} to indicate that a part of the insets has
248          * been used to offset or clip its children, and the children shouldn't worry about that
249          * part anymore.
250          *
251          * @param insets The amount to inset.
252          * @return A copy of this instance inset in the given directions.
253          * @see WindowInsets#inset
254          * @see Callback#onStart
255          */
256         @NonNull
inset(@onNull Insets insets)257         public Bounds inset(@NonNull Insets insets) {
258             return new Bounds(
259                     // TODO: refactor so that WindowInsets.insetInsets() is in a more appropriate
260                     //  place eventually.
261                     WindowInsets.insetInsets(
262                             mLowerBound, insets.left, insets.top, insets.right, insets.bottom),
263                     WindowInsets.insetInsets(
264                             mUpperBound, insets.left, insets.top, insets.right, insets.bottom));
265         }
266 
267         @Override
toString()268         public String toString() {
269             return "Bounds{lower=" + mLowerBound + " upper=" + mUpperBound + "}";
270         }
271     }
272 
273     /**
274      * Interface that allows the application to listen to animation events for windows that cause
275      * insets.
276      */
277     @SuppressLint("CallbackMethodName") // TODO(b/149430296) Should be on method, not class.
278     public abstract static class Callback {
279 
280         /**
281          * Return value for {@link #getDispatchMode()}: Dispatching of animation events should
282          * stop at this level in the view hierarchy, and no animation events should be dispatch to
283          * the subtree of the view hierarchy.
284          */
285         public static final int DISPATCH_MODE_STOP = 0;
286 
287         /**
288          * Return value for {@link #getDispatchMode()}: Dispatching of animation events should
289          * continue in the view hierarchy.
290          */
291         public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1;
292 
293         /** @hide */
294         @IntDef(prefix = { "DISPATCH_MODE_" }, value = {
295                 DISPATCH_MODE_STOP,
296                 DISPATCH_MODE_CONTINUE_ON_SUBTREE
297         })
298         @Retention(RetentionPolicy.SOURCE)
299         public @interface DispatchMode {}
300 
301         @DispatchMode
302         private final int mDispatchMode;
303 
304         /**
305          * Creates a new {@link WindowInsetsAnimation} callback with the given
306          * {@link #getDispatchMode() dispatch mode}.
307          *
308          * @param dispatchMode The dispatch mode for this callback. See {@link #getDispatchMode()}.
309          */
Callback(@ispatchMode int dispatchMode)310         public Callback(@DispatchMode int dispatchMode) {
311             mDispatchMode = dispatchMode;
312         }
313 
314         /**
315          * Retrieves the dispatch mode of this listener. Dispatch of the all animation events is
316          * hierarchical: It will starts at the root of the view hierarchy and then traverse it and
317          * invoke the callback of the specific {@link View} that is being traversed.
318          * The method may return either {@link #DISPATCH_MODE_CONTINUE_ON_SUBTREE} to indicate that
319          * animation events should be propagated to the subtree of the view hierarchy, or
320          * {@link #DISPATCH_MODE_STOP} to stop dispatching. In that case, all animation callbacks
321          * related to the animation passed in will be stopped from propagating to the subtree of the
322          * hierarchy.
323          * <p>
324          * Also note that {@link #DISPATCH_MODE_STOP} behaves the same way as
325          * returning {@link WindowInsets#CONSUMED} during the regular insets dispatch in
326          * {@link View#onApplyWindowInsets}.
327          *
328          * @return Either {@link #DISPATCH_MODE_CONTINUE_ON_SUBTREE} to indicate that dispatching of
329          *         animation events will continue to the subtree of the view hierarchy, or
330          *         {@link #DISPATCH_MODE_STOP} to indicate that animation events will stop
331          *         dispatching.
332          */
333         @DispatchMode
334         @SuppressLint("CallbackMethodName") // TODO(b/149430296) False positive: not a callback.
getDispatchMode()335         public final int getDispatchMode() {
336             return mDispatchMode;
337         }
338 
339         /**
340          * Called when an insets animation is about to start and before the views have been laid out
341          * in the end state of the animation. The ordering of events during an insets animation is
342          * the following:
343          * <p>
344          * <ul>
345          *     <li>Application calls {@link WindowInsetsController#hide(int)},
346          *     {@link WindowInsetsController#show(int)},
347          *     {@link WindowInsetsController#controlWindowInsetsAnimation}</li>
348          *     <li>onPrepare is called on the view hierarchy listeners</li>
349          *     <li>{@link View#onApplyWindowInsets} will be called with the end state of the
350          *     animation</li>
351          *     <li>View hierarchy gets laid out according to the changes the application has
352          *     requested due to the new insets being dispatched</li>
353          *     <li>{@link #onStart} is called <em>before</em> the view
354          *     hierarchy gets drawn in the new laid out state</li>
355          *     <li>{@link #onProgress} is called immediately after with the animation start
356          *     state</li>
357          *     <li>The frame gets drawn.</li>
358          * </ul>
359          * <p>
360          * This ordering allows the application to inspect the end state after the animation has
361          * finished, and then revert to the starting state of the animation in the first
362          * {@link #onProgress} callback by using post-layout view properties like {@link View#setX}
363          * and related methods.
364          * <p>
365          * Note: If the animation is application controlled by using
366          * {@link WindowInsetsController#controlWindowInsetsAnimation}, the end state of the
367          * animation is undefined as the application may decide on the end state only by passing in
368          * {@code shown} parameter when calling {@link WindowInsetsAnimationController#finish}. In
369          * this situation, the system will dispatch the insets in the opposite visibility state
370          * before the animation starts. Example: When controlling the input method with
371          * {@link WindowInsetsController#controlWindowInsetsAnimation} and the input method is
372          * currently showing, {@link View#onApplyWindowInsets} will receive a {@link WindowInsets}
373          * instance for which {@link WindowInsets#isVisible} will return {@code false} for
374          * {@link WindowInsets.Type#ime}.
375          *
376          * @param animation The animation that is about to start.
377          */
onPrepare(@onNull WindowInsetsAnimation animation)378         public void onPrepare(@NonNull WindowInsetsAnimation animation) {
379         }
380 
381         /**
382          * Called when an insets animation gets started.
383          * <p>
384          * Note that, like {@link #onProgress}, dispatch of the animation start event is
385          * hierarchical: It will starts at the root of the view hierarchy and then traverse it
386          * and invoke the callback of the specific {@link View} that is being traversed.
387          * The method may return a modified
388          * instance of the bounds by calling {@link Bounds#inset} to indicate that a part of
389          * the insets have been used to offset or clip its children, and the children shouldn't
390          * worry about that part anymore. Furthermore, if {@link #getDispatchMode()} returns
391          * {@link #DISPATCH_MODE_STOP}, children of this view will not receive the callback anymore.
392          *
393          * @param animation The animation that is about to start.
394          * @param bounds The bounds in which animation happens.
395          * @return The animation representing the part of the insets that should be dispatched to
396          *         the subtree of the hierarchy.
397          */
398         @NonNull
onStart( @onNull WindowInsetsAnimation animation, @NonNull Bounds bounds)399         public Bounds onStart(
400                 @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds) {
401             return bounds;
402         }
403 
404         /**
405          * Called when the insets change as part of running an animation. Note that even if multiple
406          * animations for different types are running, there will only be one progress callback per
407          * frame. The {@code insets} passed as an argument represents the overall state and will
408          * include all types, regardless of whether they are animating or not.
409          * <p>
410          * Note that insets dispatch is hierarchical: It will start at the root of the view
411          * hierarchy, and then traverse it and invoke the callback of the specific {@link View}
412          * being traversed. The method may return a modified instance by calling
413          * {@link WindowInsets#inset(int, int, int, int)} to indicate that a part of the insets have
414          * been used to offset or clip its children, and the children shouldn't worry about that
415          * part anymore. Furthermore, if {@link #getDispatchMode()} returns
416          * {@link #DISPATCH_MODE_STOP}, children of this view will not receive the callback anymore.
417          *
418          * @param insets The current insets.
419          * @param runningAnimations The currently running animations.
420          * @return The insets to dispatch to the subtree of the hierarchy.
421          */
422         @NonNull
onProgress(@onNull WindowInsets insets, @NonNull List<WindowInsetsAnimation> runningAnimations)423         public abstract WindowInsets onProgress(@NonNull WindowInsets insets,
424                 @NonNull List<WindowInsetsAnimation> runningAnimations);
425 
426         /**
427          * Called when an insets animation has ended.
428          *
429          * @param animation The animation that has ended. This will be the same instance
430          *                  as passed into {@link #onStart}
431          */
onEnd(@onNull WindowInsetsAnimation animation)432         public void onEnd(@NonNull WindowInsetsAnimation animation) {
433         }
434 
435     }
436 }
437