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}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} 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}. 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 {@link WindowInsets.Type}. 178 * @see #getAlpha() 179 */ setAlpha(@loatRangefrom = 0f, to = 1f) float alpha)180 public void setAlpha(@FloatRange(from = 0f, to = 1f) float alpha) { 181 mAlpha = alpha; 182 } 183 184 /** 185 * Class representing the range of an {@link WindowInsetsAnimation} 186 */ 187 public static final class Bounds { 188 189 private final Insets mLowerBound; 190 private final Insets mUpperBound; 191 Bounds(@onNull Insets lowerBound, @NonNull Insets upperBound)192 public Bounds(@NonNull Insets lowerBound, @NonNull Insets upperBound) { 193 mLowerBound = lowerBound; 194 mUpperBound = upperBound; 195 } 196 197 /** 198 * Queries the lower inset bound of the animation. If the animation is about showing or 199 * hiding a window that cause insets, the lower bound is {@link Insets#NONE} and the upper 200 * bound is the same as {@link WindowInsets#getInsets(int)} for the fully shown state. This 201 * is the same as {@link WindowInsetsAnimationController#getHiddenStateInsets} and 202 * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets 203 * invoked because of an animation that originates from 204 * {@link WindowInsetsAnimationController}. 205 * <p> 206 * However, if the size of a window that causes insets is changing, these are the 207 * lower/upper bounds of that size animation. 208 * </p> 209 * There are no overlapping animations for a specific type, but there may be multiple 210 * animations running at the same time for different inset types. 211 * 212 * @see #getUpperBound() 213 * @see WindowInsetsAnimationController#getHiddenStateInsets 214 */ 215 @NonNull getLowerBound()216 public Insets getLowerBound() { 217 return mLowerBound; 218 } 219 220 /** 221 * Queries the upper inset bound of the animation. If the animation is about showing or 222 * hiding a window that cause insets, the lower bound is {@link Insets#NONE} 223 * nd the upper bound is the same as {@link WindowInsets#getInsets(int)} for the fully 224 * shown state. This is the same as 225 * {@link WindowInsetsAnimationController#getHiddenStateInsets} and 226 * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets 227 * invoked because of an animation that originates from 228 * {@link WindowInsetsAnimationController}. 229 * <p> 230 * However, if the size of a window that causes insets is changing, these are the 231 * lower/upper bounds of that size animation. 232 * <p> 233 * There are no overlapping animations for a specific type, but there may be multiple 234 * animations running at the same time for different inset types. 235 * 236 * @see #getLowerBound() 237 * @see WindowInsetsAnimationController#getShownStateInsets 238 */ 239 @NonNull getUpperBound()240 public Insets getUpperBound() { 241 return mUpperBound; 242 } 243 244 /** 245 * Insets both the lower and upper bound by the specified insets. This is to be used in 246 * {@link Callback#onStart} to indicate that a part of the insets has 247 * been used to offset or clip its children, and the children shouldn't worry about that 248 * part anymore. 249 * 250 * @param insets The amount to inset. 251 * @return A copy of this instance inset in the given directions. 252 * @see WindowInsets#inset 253 * @see Callback#onStart 254 */ 255 @NonNull inset(@onNull Insets insets)256 public Bounds inset(@NonNull Insets insets) { 257 return new Bounds( 258 // TODO: refactor so that WindowInsets.insetInsets() is in a more appropriate 259 // place eventually. 260 WindowInsets.insetInsets( 261 mLowerBound, insets.left, insets.top, insets.right, insets.bottom), 262 WindowInsets.insetInsets( 263 mUpperBound, insets.left, insets.top, insets.right, insets.bottom)); 264 } 265 266 @Override toString()267 public String toString() { 268 return "Bounds{lower=" + mLowerBound + " upper=" + mUpperBound + "}"; 269 } 270 } 271 272 /** 273 * Interface that allows the application to listen to animation events for windows that cause 274 * insets. 275 */ 276 @SuppressLint("CallbackMethodName") // TODO(b/149430296) Should be on method, not class. 277 public abstract static class Callback { 278 279 /** 280 * Return value for {@link #getDispatchMode()}: Dispatching of animation events should 281 * stop at this level in the view hierarchy, and no animation events should be dispatch to 282 * the subtree of the view hierarchy. 283 */ 284 public static final int DISPATCH_MODE_STOP = 0; 285 286 /** 287 * Return value for {@link #getDispatchMode()}: Dispatching of animation events should 288 * continue in the view hierarchy. 289 */ 290 public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1; 291 292 /** @hide */ 293 @IntDef(prefix = { "DISPATCH_MODE_" }, value = { 294 DISPATCH_MODE_STOP, 295 DISPATCH_MODE_CONTINUE_ON_SUBTREE 296 }) 297 @Retention(RetentionPolicy.SOURCE) 298 public @interface DispatchMode {} 299 300 @DispatchMode 301 private final int mDispatchMode; 302 303 /** 304 * Creates a new {@link WindowInsetsAnimation} callback with the given 305 * {@link #getDispatchMode() dispatch mode}. 306 * 307 * @param dispatchMode The dispatch mode for this callback. See {@link #getDispatchMode()}. 308 */ Callback(@ispatchMode int dispatchMode)309 public Callback(@DispatchMode int dispatchMode) { 310 mDispatchMode = dispatchMode; 311 } 312 313 /** 314 * Retrieves the dispatch mode of this listener. Dispatch of the all animation events is 315 * hierarchical: It will starts at the root of the view hierarchy and then traverse it and 316 * invoke the callback of the specific {@link View} that is being traversed. 317 * The method may return either {@link #DISPATCH_MODE_CONTINUE_ON_SUBTREE} to indicate that 318 * animation events should be propagated to the subtree of the view hierarchy, or 319 * {@link #DISPATCH_MODE_STOP} to stop dispatching. In that case, all animation callbacks 320 * related to the animation passed in will be stopped from propagating to the subtree of the 321 * hierarchy. 322 * <p> 323 * Also note that {@link #DISPATCH_MODE_STOP} behaves the same way as 324 * returning {@link WindowInsets#CONSUMED} during the regular insets dispatch in 325 * {@link View#onApplyWindowInsets}. 326 * 327 * @return Either {@link #DISPATCH_MODE_CONTINUE_ON_SUBTREE} to indicate that dispatching of 328 * animation events will continue to the subtree of the view hierarchy, or 329 * {@link #DISPATCH_MODE_STOP} to indicate that animation events will stop 330 * dispatching. 331 */ 332 @DispatchMode 333 @SuppressLint("CallbackMethodName") // TODO(b/149430296) False positive: not a callback. getDispatchMode()334 public final int getDispatchMode() { 335 return mDispatchMode; 336 } 337 338 /** 339 * Called when an insets animation is about to start and before the views have been laid out 340 * in the end state of the animation. The ordering of events during an insets animation is 341 * the following: 342 * <p> 343 * <ul> 344 * <li>Application calls {@link WindowInsetsController#hide(int)}, 345 * {@link WindowInsetsController#show(int)}, 346 * {@link WindowInsetsController#controlWindowInsetsAnimation}</li> 347 * <li>onPrepare is called on the view hierarchy listeners</li> 348 * <li>{@link View#onApplyWindowInsets} will be called with the end state of the 349 * animation</li> 350 * <li>View hierarchy gets laid out according to the changes the application has 351 * requested due to the new insets being dispatched</li> 352 * <li>{@link #onStart} is called <em>before</em> the view 353 * hierarchy gets drawn in the new laid out state</li> 354 * <li>{@link #onProgress} is called immediately after with the animation start 355 * state</li> 356 * <li>The frame gets drawn.</li> 357 * </ul> 358 * <p> 359 * This ordering allows the application to inspect the end state after the animation has 360 * finished, and then revert to the starting state of the animation in the first 361 * {@link #onProgress} callback by using post-layout view properties like {@link View#setX} 362 * and related methods. 363 * 364 * <p>Note that the animation might be cancelled before {@link #onStart} is dispatched. On 365 * {@link android.os.Build.VERSION_CODES#S S} and later, {@link #onEnd} is immediately 366 * dispatched without an {@link #onStart} in that case. 367 * On {@link android.os.Build.VERSION_CODES#R R}, no callbacks are dispatched after 368 * {@code #onPrepare} for such an animation. 369 * 370 * <p> 371 * Note: If the animation is application controlled by using 372 * {@link WindowInsetsController#controlWindowInsetsAnimation}, the end state of the 373 * animation is undefined as the application may decide on the end state only by passing in 374 * {@code shown} parameter when calling {@link WindowInsetsAnimationController#finish}. In 375 * this situation, the system will dispatch the insets in the opposite visibility state 376 * before the animation starts. Example: When controlling the input method with 377 * {@link WindowInsetsController#controlWindowInsetsAnimation} and the input method is 378 * currently showing, {@link View#onApplyWindowInsets} will receive a {@link WindowInsets} 379 * instance for which {@link WindowInsets#isVisible} will return {@code false} for 380 * {@link WindowInsets.Type#ime}. 381 * 382 * @param animation The animation that is about to start. 383 */ onPrepare(@onNull WindowInsetsAnimation animation)384 public void onPrepare(@NonNull WindowInsetsAnimation animation) { 385 } 386 387 /** 388 * Called when an insets animation gets started. 389 * <p> 390 * Note that, like {@link #onProgress}, dispatch of the animation start event is 391 * hierarchical: It will starts at the root of the view hierarchy and then traverse it 392 * and invoke the callback of the specific {@link View} that is being traversed. 393 * The method may return a modified 394 * instance of the bounds by calling {@link Bounds#inset} to indicate that a part of 395 * the insets have been used to offset or clip its children, and the children shouldn't 396 * worry about that part anymore. Furthermore, if {@link #getDispatchMode()} returns 397 * {@link #DISPATCH_MODE_STOP}, children of this view will not receive the callback anymore. 398 * 399 * @param animation The animation that is about to start. 400 * @param bounds The bounds in which animation happens. 401 * @return The animation representing the part of the insets that should be dispatched to 402 * the subtree of the hierarchy. 403 */ 404 @NonNull onStart( @onNull WindowInsetsAnimation animation, @NonNull Bounds bounds)405 public Bounds onStart( 406 @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds) { 407 return bounds; 408 } 409 410 /** 411 * Called when the insets change as part of running an animation. Note that even if multiple 412 * animations for different types are running, there will only be one progress callback per 413 * frame. The {@code insets} passed as an argument represents the overall state and will 414 * include all types, regardless of whether they are animating or not. 415 * <p> 416 * Note that insets dispatch is hierarchical: It will start at the root of the view 417 * hierarchy, and then traverse it and invoke the callback of the specific {@link View} 418 * being traversed. The method may return a modified instance by calling 419 * {@link WindowInsets#inset(int, int, int, int)} to indicate that a part of the insets have 420 * been used to offset or clip its children, and the children shouldn't worry about that 421 * part anymore. Furthermore, if {@link #getDispatchMode()} returns 422 * {@link #DISPATCH_MODE_STOP}, children of this view will not receive the callback anymore. 423 * 424 * @param insets The current insets. 425 * @param runningAnimations The currently running animations. 426 * @return The insets to dispatch to the subtree of the hierarchy. 427 */ 428 @NonNull onProgress(@onNull WindowInsets insets, @NonNull List<WindowInsetsAnimation> runningAnimations)429 public abstract WindowInsets onProgress(@NonNull WindowInsets insets, 430 @NonNull List<WindowInsetsAnimation> runningAnimations); 431 432 /** 433 * Called when an insets animation has ended. 434 * 435 * @param animation The animation that has ended. This will be the same instance 436 * as passed into {@link #onStart} 437 */ onEnd(@onNull WindowInsetsAnimation animation)438 public void onEnd(@NonNull WindowInsetsAnimation animation) { 439 } 440 441 } 442 } 443