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