Merge changes I88f7f7e5,If6c1153d
* changes: Expand docs for animation listener A brave new world for window insets (11/n)
This commit is contained in:
@@ -22,6 +22,7 @@ import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
|
||||
import android.annotation.TestApi;
|
||||
import android.annotation.UnsupportedAppUsage;
|
||||
import android.graphics.FrameInfo;
|
||||
import android.graphics.Insets;
|
||||
import android.hardware.display.DisplayManagerGlobal;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
@@ -199,7 +200,7 @@ public final class Choreographer {
|
||||
* @hide
|
||||
*/
|
||||
private static final String[] CALLBACK_TRACE_TITLES = {
|
||||
"input", "animation", "traversal", "commit"
|
||||
"input", "animation", "insets_animation", "traversal", "commit"
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -209,18 +210,33 @@ public final class Choreographer {
|
||||
public static final int CALLBACK_INPUT = 0;
|
||||
|
||||
/**
|
||||
* Callback type: Animation callback. Runs before traversals.
|
||||
* Callback type: Animation callback. Runs before {@link #CALLBACK_INSETS_ANIMATION}.
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public static final int CALLBACK_ANIMATION = 1;
|
||||
|
||||
/**
|
||||
* Callback type: Animation callback to handle inset updates. This is separate from
|
||||
* {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
|
||||
* {@link WindowInsetsAnimationController#changeInsets} for multiple ongoing animations but then
|
||||
* update the whole view system with a single callback to {@link View#dispatchWindowInsetsAnimationProgress}
|
||||
* that contains all the combined updated insets.
|
||||
* <p>
|
||||
* Both input and animation may change insets, so we need to run this after these callbacks, but
|
||||
* before traversals.
|
||||
* <p>
|
||||
* Runs before traversals.
|
||||
* @hide
|
||||
*/
|
||||
public static final int CALLBACK_INSETS_ANIMATION = 2;
|
||||
|
||||
/**
|
||||
* Callback type: Traversal callback. Handles layout and draw. Runs
|
||||
* after all other asynchronous messages have been handled.
|
||||
* @hide
|
||||
*/
|
||||
public static final int CALLBACK_TRAVERSAL = 2;
|
||||
public static final int CALLBACK_TRAVERSAL = 3;
|
||||
|
||||
/**
|
||||
* Callback type: Commit callback. Handles post-draw operations for the frame.
|
||||
@@ -232,7 +248,7 @@ public final class Choreographer {
|
||||
* to the view hierarchy state) actually took effect.
|
||||
* @hide
|
||||
*/
|
||||
public static final int CALLBACK_COMMIT = 3;
|
||||
public static final int CALLBACK_COMMIT = 4;
|
||||
|
||||
private static final int CALLBACK_LAST = CALLBACK_COMMIT;
|
||||
|
||||
@@ -704,6 +720,7 @@ public final class Choreographer {
|
||||
|
||||
mFrameInfo.markAnimationsStart();
|
||||
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
|
||||
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
|
||||
|
||||
mFrameInfo.markPerformTraversalsStart();
|
||||
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
|
||||
|
||||
@@ -45,7 +45,9 @@ import java.util.function.Supplier;
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public class InsetsAnimationControlImpl implements WindowInsetsAnimationController {
|
||||
public class InsetsAnimationControlImpl implements WindowInsetsAnimationController {
|
||||
|
||||
private final Rect mTmpFrame = new Rect();
|
||||
|
||||
private final WindowInsetsAnimationControlListener mListener;
|
||||
private final SparseArray<InsetsSourceConsumer> mConsumers;
|
||||
@@ -61,19 +63,23 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
|
||||
private final InsetsState mInitialInsetsState;
|
||||
private final @InsetType int mTypes;
|
||||
private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
|
||||
|
||||
private final InsetsController mController;
|
||||
private final WindowInsetsAnimationListener.InsetsAnimation mAnimation;
|
||||
private Insets mCurrentInsets;
|
||||
private Insets mPendingInsets;
|
||||
|
||||
@VisibleForTesting
|
||||
public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
|
||||
InsetsState state, WindowInsetsAnimationControlListener listener,
|
||||
@InsetType int types,
|
||||
Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier) {
|
||||
Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier,
|
||||
InsetsController controller) {
|
||||
mConsumers = consumers;
|
||||
mListener = listener;
|
||||
mTypes = types;
|
||||
mTransactionApplierSupplier = transactionApplierSupplier;
|
||||
mInitialInsetsState = new InsetsState(state);
|
||||
mController = controller;
|
||||
mInitialInsetsState = new InsetsState(state, true /* copySources */);
|
||||
mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
|
||||
mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */,
|
||||
null /* typeSideMap */);
|
||||
@@ -83,6 +89,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
|
||||
|
||||
// TODO: Check for controllability first and wait for IME if needed.
|
||||
listener.onReady(this, types);
|
||||
|
||||
mAnimation = new WindowInsetsAnimationListener.InsetsAnimation(mTypes, mHiddenInsets,
|
||||
mShownInsets);
|
||||
mController.dispatchAnimationStarted(mAnimation);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,29 +118,35 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
|
||||
|
||||
@Override
|
||||
public void changeInsets(Insets insets) {
|
||||
insets = sanitize(insets);
|
||||
final Insets offset = Insets.subtract(mShownInsets, insets);
|
||||
mPendingInsets = sanitize(insets);
|
||||
mController.scheduleApplyChangeInsets();
|
||||
}
|
||||
|
||||
void applyChangeInsets(InsetsState state) {
|
||||
final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
|
||||
ArrayList<SurfaceParams> params = new ArrayList<>();
|
||||
if (offset.left != 0) {
|
||||
updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params);
|
||||
updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params, state);
|
||||
}
|
||||
if (offset.top != 0) {
|
||||
updateLeashesForSide(INSET_SIDE_TOP, offset.top, params);
|
||||
updateLeashesForSide(INSET_SIDE_TOP, offset.top, params, state);
|
||||
}
|
||||
if (offset.right != 0) {
|
||||
updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params);
|
||||
updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params, state);
|
||||
}
|
||||
if (offset.bottom != 0) {
|
||||
updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params);
|
||||
updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params, state);
|
||||
}
|
||||
SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
|
||||
applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
|
||||
mCurrentInsets = insets;
|
||||
mCurrentInsets = mPendingInsets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish(int shownTypes) {
|
||||
// TODO
|
||||
|
||||
mController.dispatchAnimationFinished(mAnimation);
|
||||
}
|
||||
|
||||
private Insets calculateInsets(InsetsState state, Rect frame,
|
||||
@@ -146,7 +162,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
|
||||
@Nullable @InsetSide SparseIntArray typeSideMap) {
|
||||
return state.calculateInsets(frame, false /* isScreenRound */,
|
||||
false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap)
|
||||
.getSystemWindowInsets();
|
||||
.getInsets(mTypes);
|
||||
}
|
||||
|
||||
private Insets sanitize(Insets insets) {
|
||||
@@ -154,7 +170,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
|
||||
}
|
||||
|
||||
private void updateLeashesForSide(@InsetSide int side, int inset,
|
||||
ArrayList<SurfaceParams> surfaceParams) {
|
||||
ArrayList<SurfaceParams> surfaceParams, InsetsState state) {
|
||||
ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
|
||||
// TODO: Implement behavior when inset spans over multiple types
|
||||
for (int i = items.size() - 1; i >= 0; i--) {
|
||||
@@ -162,24 +178,32 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
|
||||
final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
|
||||
final SurfaceControl leash = consumer.getControl().getLeash();
|
||||
mTmpMatrix.setTranslate(source.getFrame().left, source.getFrame().top);
|
||||
addTranslationToMatrix(side, inset, mTmpMatrix);
|
||||
|
||||
mTmpFrame.set(source.getFrame());
|
||||
addTranslationToMatrix(side, inset, mTmpMatrix, mTmpFrame);
|
||||
|
||||
state.getSource(source.getType()).setFrame(mTmpFrame);
|
||||
surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f));
|
||||
}
|
||||
}
|
||||
|
||||
private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m) {
|
||||
private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m, Rect frame) {
|
||||
switch (side) {
|
||||
case INSET_SIDE_LEFT:
|
||||
m.postTranslate(-inset, 0);
|
||||
frame.offset(-inset, 0);
|
||||
break;
|
||||
case INSET_SIDE_TOP:
|
||||
m.postTranslate(0, -inset);
|
||||
frame.offset(0, -inset);
|
||||
break;
|
||||
case INSET_SIDE_RIGHT:
|
||||
m.postTranslate(inset, 0);
|
||||
frame.offset(inset, 0);
|
||||
break;
|
||||
case INSET_SIDE_BOTTOM:
|
||||
m.postTranslate(0, inset);
|
||||
frame.offset(0, inset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.view;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArraySet;
|
||||
@@ -49,9 +50,29 @@ public class InsetsController implements WindowInsetsController {
|
||||
|
||||
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
|
||||
private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
|
||||
private WindowInsets mLastInsets;
|
||||
|
||||
private boolean mAnimCallbackScheduled;
|
||||
|
||||
private final Runnable mAnimCallback;
|
||||
|
||||
public InsetsController(ViewRootImpl viewRoot) {
|
||||
mViewRoot = viewRoot;
|
||||
mAnimCallback = () -> {
|
||||
mAnimCallbackScheduled = false;
|
||||
if (mAnimationControls.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
InsetsState state = new InsetsState(mState, true /* copySources */);
|
||||
for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
|
||||
mAnimationControls.get(i).applyChangeInsets(state);
|
||||
}
|
||||
WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(),
|
||||
mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(),
|
||||
null /* typeSideMap */);
|
||||
mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets);
|
||||
};
|
||||
}
|
||||
|
||||
void onFrameChanged(Rect frame) {
|
||||
@@ -82,8 +103,9 @@ public class InsetsController implements WindowInsetsController {
|
||||
@VisibleForTesting
|
||||
public WindowInsets calculateInsets(boolean isScreenRound,
|
||||
boolean alwaysConsumeNavBar, DisplayCutout cutout) {
|
||||
return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
|
||||
mLastInsets = mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
|
||||
null /* typeSideMap */);
|
||||
return mLastInsets;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,7 +170,7 @@ public class InsetsController implements WindowInsetsController {
|
||||
}
|
||||
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
|
||||
mFrame, mState, listener, types,
|
||||
() -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView));
|
||||
() -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
|
||||
mAnimationControls.add(controller);
|
||||
}
|
||||
|
||||
@@ -200,4 +222,20 @@ public class InsetsController implements WindowInsetsController {
|
||||
pw.println(prefix); pw.println("InsetsController:");
|
||||
mState.dump(prefix + " ", pw);
|
||||
}
|
||||
|
||||
void dispatchAnimationStarted(WindowInsetsAnimationListener.InsetsAnimation animation) {
|
||||
mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation);
|
||||
}
|
||||
|
||||
void dispatchAnimationFinished(WindowInsetsAnimationListener.InsetsAnimation animation) {
|
||||
mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
|
||||
}
|
||||
|
||||
void scheduleApplyChangeInsets() {
|
||||
if (!mAnimCallbackScheduled) {
|
||||
mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION,
|
||||
mAnimCallback, null /* token*/);
|
||||
mAnimCallbackScheduled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,10 @@ public class InsetsState implements Parcelable {
|
||||
set(copy);
|
||||
}
|
||||
|
||||
public InsetsState(InsetsState copy, boolean copySources) {
|
||||
set(copy, copySources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates {@link WindowInsets} based on the current source configuration.
|
||||
*
|
||||
|
||||
@@ -96,6 +96,7 @@ import android.view.AccessibilityIterators.ParagraphTextSegmentIterator;
|
||||
import android.view.AccessibilityIterators.TextSegmentIterator;
|
||||
import android.view.AccessibilityIterators.WordTextSegmentIterator;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.WindowInsetsAnimationListener.InsetsAnimation;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityEventSource;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
@@ -4547,6 +4548,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
OnCapturedPointerListener mOnCapturedPointerListener;
|
||||
|
||||
private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;
|
||||
|
||||
private WindowInsetsAnimationListener mWindowInsetsAnimationListener;
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
@@ -10487,6 +10490,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link WindowInsetsAnimationListener} to be notified about animations of windows that
|
||||
* cause insets.
|
||||
*
|
||||
* @param listener The listener to set.
|
||||
* @hide pending unhide
|
||||
*/
|
||||
public void setWindowInsetsAnimationListener(WindowInsetsAnimationListener listener) {
|
||||
getListenerInfo().mWindowInsetsAnimationListener = listener;
|
||||
}
|
||||
|
||||
void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) {
|
||||
if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
|
||||
mListenerInfo.mWindowInsetsAnimationListener.onStarted(animation);
|
||||
}
|
||||
}
|
||||
|
||||
WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) {
|
||||
if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
|
||||
return mListenerInfo.mWindowInsetsAnimationListener.onProgress(insets);
|
||||
} else {
|
||||
return insets;
|
||||
}
|
||||
}
|
||||
|
||||
void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
|
||||
if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) {
|
||||
mListenerInfo.mWindowInsetsAnimationListener.onFinished(animation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the view's coordinate within the surface.
|
||||
*
|
||||
|
||||
@@ -51,6 +51,7 @@ import android.util.Pools;
|
||||
import android.util.Pools.SynchronizedPool;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.WindowInsetsAnimationListener.InsetsAnimation;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
@@ -7139,6 +7140,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
return insets;
|
||||
}
|
||||
|
||||
@Override
|
||||
void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) {
|
||||
super.dispatchWindowInsetsAnimationStarted(animation);
|
||||
final int count = getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
getChildAt(i).dispatchWindowInsetsAnimationStarted(animation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) {
|
||||
insets = super.dispatchWindowInsetsAnimationProgress(insets);
|
||||
final int count = getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
getChildAt(i).dispatchWindowInsetsAnimationProgress(insets);
|
||||
}
|
||||
return insets;
|
||||
}
|
||||
|
||||
@Override
|
||||
void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
|
||||
super.dispatchWindowInsetsAnimationFinished(animation);
|
||||
final int count = getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
getChildAt(i).dispatchWindowInsetsAnimationFinished(animation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the animation listener to which layout animation events are
|
||||
* sent.
|
||||
|
||||
@@ -19,6 +19,7 @@ package android.view;
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Insets;
|
||||
import android.view.WindowInsets.Type.InsetType;
|
||||
import android.view.WindowInsetsAnimationListener.InsetsAnimation;
|
||||
|
||||
/**
|
||||
* Interface to control a window inset animation frame-by-frame.
|
||||
@@ -28,8 +29,13 @@ public interface WindowInsetsAnimationController {
|
||||
|
||||
/**
|
||||
* Retrieves the {@link Insets} when the windows this animation is controlling are fully hidden.
|
||||
* <p>
|
||||
* If there are any animation listeners registered, this value is the same as
|
||||
* {@link InsetsAnimation#getLowerBound()} that will be passed into the callbacks.
|
||||
*
|
||||
* @return Insets when the windows this animation is controlling are fully hidden.
|
||||
*
|
||||
* @see InsetsAnimation#getLowerBound()
|
||||
*/
|
||||
@NonNull Insets getHiddenStateInsets();
|
||||
|
||||
@@ -38,8 +44,13 @@ public interface WindowInsetsAnimationController {
|
||||
* <p>
|
||||
* In case the size of a window causing insets is changing in the middle of the animation, we
|
||||
* execute that height change after this animation has finished.
|
||||
* <p>
|
||||
* If there are any animation listeners registered, this value is the same as
|
||||
* {@link InsetsAnimation#getUpperBound()} that will be passed into the callbacks.
|
||||
*
|
||||
* @return Insets when the windows this animation is controlling are fully shown.
|
||||
*
|
||||
* @see InsetsAnimation#getUpperBound()
|
||||
*/
|
||||
@NonNull Insets getShownStateInsets();
|
||||
|
||||
@@ -59,8 +70,11 @@ public interface WindowInsetsAnimationController {
|
||||
* <p>
|
||||
* Note that this will <b>not</b> inform the view system of a full inset change via
|
||||
* {@link View#dispatchApplyWindowInsets} in order to avoid a full layout pass during the
|
||||
* animation. If you'd like to animate views during a window inset animation, use
|
||||
* TODO add link to animation listeners.
|
||||
* animation. If you'd like to animate views during a window inset animation, register a
|
||||
* {@link WindowInsetsAnimationListener} by calling
|
||||
* {@link View#setWindowInsetsAnimationListener(WindowInsetsAnimationListener)} that will be
|
||||
* notified about any insets change via {@link WindowInsetsAnimationListener#onProgress} during
|
||||
* the animation.
|
||||
* <p>
|
||||
* {@link View#dispatchApplyWindowInsets} will instead be called once the animation has
|
||||
* finished, i.e. once {@link #finish} has been called.
|
||||
@@ -70,6 +84,9 @@ public interface WindowInsetsAnimationController {
|
||||
* the resulting insets of that configuration will match the passed in parameter.
|
||||
* Note that these insets are being clamped to the range from
|
||||
* {@link #getHiddenStateInsets} to {@link #getShownStateInsets}
|
||||
*
|
||||
* @see WindowInsetsAnimationListener
|
||||
* @see View#setWindowInsetsAnimationListener(WindowInsetsAnimationListener)
|
||||
*/
|
||||
void changeInsets(@NonNull Insets insets);
|
||||
|
||||
|
||||
122
core/java/android/view/WindowInsetsAnimationListener.java
Normal file
122
core/java/android/view/WindowInsetsAnimationListener.java
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.graphics.Insets;
|
||||
|
||||
/**
|
||||
* Interface that allows the application to listen to animation events for windows that cause
|
||||
* insets.
|
||||
* @hide pending unhide
|
||||
*/
|
||||
public interface WindowInsetsAnimationListener {
|
||||
|
||||
/**
|
||||
* Called when an inset animation gets started.
|
||||
*
|
||||
* @param animation The animation that is about to start.
|
||||
*/
|
||||
void onStarted(InsetsAnimation animation);
|
||||
|
||||
/**
|
||||
* 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 callback 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.
|
||||
*
|
||||
* @param insets The current insets.
|
||||
* @return The insets to dispatch to the subtree of the hierarchy.
|
||||
*/
|
||||
WindowInsets onProgress(WindowInsets insets);
|
||||
|
||||
/**
|
||||
* Called when an inset animation has finished.
|
||||
*
|
||||
* @param animation The animation that has finished running.
|
||||
*/
|
||||
void onFinished(InsetsAnimation animation);
|
||||
|
||||
/**
|
||||
* Class representing an animation of a set of windows that cause insets.
|
||||
*/
|
||||
class InsetsAnimation {
|
||||
|
||||
private final @WindowInsets.Type.InsetType int mTypeMask;
|
||||
private final Insets mLowerBound;
|
||||
private final Insets mUpperBound;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
InsetsAnimation(int typeMask, Insets lowerBound, Insets upperBound) {
|
||||
mTypeMask = typeMask;
|
||||
mLowerBound = lowerBound;
|
||||
mUpperBound = upperBound;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The bitmask of {@link WindowInsets.Type.InsetType}s that are animating.
|
||||
*/
|
||||
public @WindowInsets.Type.InsetType int getTypeMask() {
|
||||
return mTypeMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 two animations
|
||||
* running at the same time for different inset types.
|
||||
*
|
||||
* @see #getUpperBound()
|
||||
* @see WindowInsetsAnimationController#getHiddenStateInsets
|
||||
* TODO: It's a bit weird that these are global per window but onProgress is hierarchical.
|
||||
* TODO: If multiple types are animating, querying the bound per type isn't possible. Should
|
||||
* we:
|
||||
* 1. Offer bounds by type here?
|
||||
* 2. Restrict one animation to one single type only?
|
||||
* Returning WindowInsets here isn't feasible in case of overlapping animations: We can't
|
||||
* fill in the insets for the types from the other animation into the WindowInsets object
|
||||
* as it's changing as well.
|
||||
*/
|
||||
public Insets getLowerBound() {
|
||||
return mLowerBound;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getLowerBound()
|
||||
* @see WindowInsetsAnimationController#getShownStateInsets
|
||||
*/
|
||||
public Insets getUpperBound() {
|
||||
return mUpperBound;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package android.view;
|
||||
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
|
||||
import static android.view.InsetsState.TYPE_TOP_BAR;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.graphics.Insets;
|
||||
@@ -83,7 +84,7 @@ public class InsetsAnimationControlImplTest {
|
||||
consumers.put(TYPE_NAVIGATION_BAR, navConsumer);
|
||||
mController = new InsetsAnimationControlImpl(consumers,
|
||||
new Rect(0, 0, 500, 500), state, mMockListener, WindowInsets.Type.systemBars(),
|
||||
() -> mMockTransactionApplier);
|
||||
() -> mMockTransactionApplier, mock(InsetsController.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user