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