A brave new world for window insets (5/n)
Implement controlWindowInsetsAnimation Based on the leashes we have on the client, and the insets the client has requested, we are able to move the surfaces around such that the resulting insets will match what the client requested. Bug: 118118435 Change-Id: I0616e53455a6544aaf374c1b0eb10e258aced21d
This commit is contained in:
@@ -54,6 +54,13 @@ public class SparseSetArray<T> {
|
||||
return set.contains(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the set of items at index n
|
||||
*/
|
||||
public ArraySet<T> get(int n) {
|
||||
return mData.get(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a value from index n.
|
||||
* @return TRUE when the value existed at the given index and removed, FALSE otherwise.
|
||||
|
||||
197
core/java/android/view/InsetsAnimationControlImpl.java
Normal file
197
core/java/android/view/InsetsAnimationControlImpl.java
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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 static android.view.InsetsState.INSET_SIDE_BOTTOM;
|
||||
import static android.view.InsetsState.INSET_SIDE_LEFT;
|
||||
import static android.view.InsetsState.INSET_SIDE_RIGHT;
|
||||
import static android.view.InsetsState.INSET_SIDE_TOP;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.os.UidProto.Sync;
|
||||
import android.util.ArraySet;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.SparseSetArray;
|
||||
import android.view.InsetsState.InsetSide;
|
||||
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
|
||||
import android.view.WindowInsets.Type.InsetType;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Implements {@link WindowInsetsAnimationController}
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public class InsetsAnimationControlImpl implements WindowInsetsAnimationController {
|
||||
|
||||
private final WindowInsetsAnimationControlListener mListener;
|
||||
private final SparseArray<InsetsSourceConsumer> mConsumers;
|
||||
private final SparseIntArray mTypeSideMap = new SparseIntArray();
|
||||
private final SparseSetArray<InsetsSourceConsumer> mSideSourceMap = new SparseSetArray<>();
|
||||
|
||||
/** @see WindowInsetsAnimationController#getHiddenStateInsets */
|
||||
private final Insets mHiddenInsets;
|
||||
|
||||
/** @see WindowInsetsAnimationController#getShownStateInsets */
|
||||
private final Insets mShownInsets;
|
||||
private final Matrix mTmpMatrix = new Matrix();
|
||||
private final InsetsState mInitialInsetsState;
|
||||
private final @InsetType int mTypes;
|
||||
private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
|
||||
|
||||
private Insets mCurrentInsets;
|
||||
|
||||
@VisibleForTesting
|
||||
public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
|
||||
InsetsState state, WindowInsetsAnimationControlListener listener,
|
||||
@InsetType int types,
|
||||
Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier) {
|
||||
mConsumers = consumers;
|
||||
mListener = listener;
|
||||
mTypes = types;
|
||||
mTransactionApplierSupplier = transactionApplierSupplier;
|
||||
mInitialInsetsState = new InsetsState(state);
|
||||
mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
|
||||
mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */,
|
||||
null /* typeSideMap */);
|
||||
mShownInsets = calculateInsets(mInitialInsetsState, frame, consumers, true /* shown */,
|
||||
mTypeSideMap);
|
||||
buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mConsumers);
|
||||
|
||||
// TODO: Check for controllability first and wait for IME if needed.
|
||||
listener.onReady(this, types);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getHiddenStateInsets() {
|
||||
return mHiddenInsets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getShownStateInsets() {
|
||||
return mShownInsets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getCurrentInsets() {
|
||||
return mCurrentInsets;
|
||||
}
|
||||
|
||||
@Override
|
||||
@InsetType
|
||||
public int getTypes() {
|
||||
return mTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeInsets(Insets insets) {
|
||||
insets = sanitize(insets);
|
||||
final Insets offset = Insets.subtract(mShownInsets, insets);
|
||||
ArrayList<SurfaceParams> params = new ArrayList<>();
|
||||
if (offset.left != 0) {
|
||||
updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params);
|
||||
}
|
||||
if (offset.top != 0) {
|
||||
updateLeashesForSide(INSET_SIDE_TOP, offset.top, params);
|
||||
}
|
||||
if (offset.right != 0) {
|
||||
updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params);
|
||||
}
|
||||
if (offset.bottom != 0) {
|
||||
updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params);
|
||||
}
|
||||
SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
|
||||
applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
|
||||
mCurrentInsets = insets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish(int shownTypes) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private Insets calculateInsets(InsetsState state, Rect frame,
|
||||
SparseArray<InsetsSourceConsumer> consumers, boolean shown,
|
||||
@Nullable @InsetSide SparseIntArray typeSideMap) {
|
||||
for (int i = consumers.size() - 1; i >= 0; i--) {
|
||||
state.getSource(consumers.valueAt(i).getType()).setVisible(shown);
|
||||
}
|
||||
return getInsetsFromState(state, frame, typeSideMap);
|
||||
}
|
||||
|
||||
private Insets getInsetsFromState(InsetsState state, Rect frame,
|
||||
@Nullable @InsetSide SparseIntArray typeSideMap) {
|
||||
return state.calculateInsets(frame, false /* isScreenRound */,
|
||||
false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap)
|
||||
.getSystemWindowInsets();
|
||||
}
|
||||
|
||||
private Insets sanitize(Insets insets) {
|
||||
return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets);
|
||||
}
|
||||
|
||||
private void updateLeashesForSide(@InsetSide int side, int inset,
|
||||
ArrayList<SurfaceParams> surfaceParams) {
|
||||
ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
|
||||
// TODO: Implement behavior when inset spans over multiple types
|
||||
for (int i = items.size() - 1; i >= 0; i--) {
|
||||
final InsetsSourceConsumer consumer = items.valueAt(i);
|
||||
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);
|
||||
surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f));
|
||||
}
|
||||
}
|
||||
|
||||
private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m) {
|
||||
switch (side) {
|
||||
case INSET_SIDE_LEFT:
|
||||
m.postTranslate(-inset, 0);
|
||||
break;
|
||||
case INSET_SIDE_TOP:
|
||||
m.postTranslate(0, -inset);
|
||||
break;
|
||||
case INSET_SIDE_RIGHT:
|
||||
m.postTranslate(inset, 0);
|
||||
break;
|
||||
case INSET_SIDE_BOTTOM:
|
||||
m.postTranslate(0, inset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildTypeSourcesMap(SparseIntArray typeSideMap,
|
||||
SparseSetArray<InsetsSourceConsumer> sideSourcesMap,
|
||||
SparseArray<InsetsSourceConsumer> consumers) {
|
||||
for (int i = typeSideMap.size() - 1; i >= 0; i--) {
|
||||
int type = typeSideMap.keyAt(i);
|
||||
int side = typeSideMap.valueAt(i);
|
||||
sideSourcesMap.add(side, consumers.get(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.view.InsetsState.InternalInsetType;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Implements {@link WindowInsetsController} on the client.
|
||||
@@ -41,6 +42,7 @@ public class InsetsController implements WindowInsetsController {
|
||||
private final ViewRootImpl mViewRoot;
|
||||
|
||||
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
|
||||
private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
|
||||
|
||||
public InsetsController(ViewRootImpl viewRoot) {
|
||||
mViewRoot = viewRoot;
|
||||
@@ -67,9 +69,11 @@ public class InsetsController implements WindowInsetsController {
|
||||
/**
|
||||
* @see InsetsState#calculateInsets
|
||||
*/
|
||||
WindowInsets calculateInsets(boolean isScreenRound,
|
||||
@VisibleForTesting
|
||||
public WindowInsets calculateInsets(boolean isScreenRound,
|
||||
boolean alwaysConsumeNavBar, DisplayCutout cutout) {
|
||||
return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout);
|
||||
return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
|
||||
null /* typeSideMap */);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,6 +120,28 @@ public class InsetsController implements WindowInsetsController {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controlWindowInsetsAnimation(@InsetType int types,
|
||||
WindowInsetsAnimationControlListener listener) {
|
||||
|
||||
// TODO: Check whether we already have a controller.
|
||||
final ArraySet<Integer> internalTypes = mState.toInternalType(types);
|
||||
final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
|
||||
for (int i = internalTypes.size() - 1; i >= 0; i--) {
|
||||
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
|
||||
if (consumer.getControl() != null) {
|
||||
consumers.put(consumer.getType(), consumer);
|
||||
} else {
|
||||
// TODO: Let calling app know it's not possible, or wait
|
||||
// TODO: Remove it from types
|
||||
}
|
||||
}
|
||||
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
|
||||
mFrame, mState, listener, types,
|
||||
() -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView));
|
||||
mAnimationControls.add(controller);
|
||||
}
|
||||
|
||||
private void applyLocalVisibilityOverride() {
|
||||
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
|
||||
final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
|
||||
@@ -134,7 +160,8 @@ public class InsetsController implements WindowInsetsController {
|
||||
return controller;
|
||||
}
|
||||
|
||||
void notifyVisibilityChanged() {
|
||||
@VisibleForTesting
|
||||
public void notifyVisibilityChanged() {
|
||||
mViewRoot.notifyInsetsChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,8 @@ public class InsetsSource implements Parcelable {
|
||||
*
|
||||
* @param relativeFrame The frame to calculate the insets relative to.
|
||||
* @param ignoreVisibility If true, always reports back insets even if source isn't visible.
|
||||
* @return The resulting insets.
|
||||
* @return The resulting insets. The contract is that only one side will be occupied by a
|
||||
* source.
|
||||
*/
|
||||
public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
|
||||
if (!ignoreVisibility && !mVisible) {
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
package android.view;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.WindowInsets.Type;
|
||||
import android.view.WindowInsets.Type.InsetType;
|
||||
|
||||
@@ -77,11 +80,30 @@ public class InsetsState implements Parcelable {
|
||||
/** A shelf is the same as the navigation bar. */
|
||||
public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(prefix = "INSET_SIDE", value = {
|
||||
INSET_SIDE_LEFT,
|
||||
INSET_SIDE_TOP,
|
||||
INSET_SIDE_RIGHT,
|
||||
INSET_SIDE_BOTTOM,
|
||||
INSET_SIDE_UNKNWON
|
||||
})
|
||||
public @interface InsetSide {}
|
||||
static final int INSET_SIDE_LEFT = 0;
|
||||
static final int INSET_SIDE_TOP = 1;
|
||||
static final int INSET_SIDE_RIGHT = 2;
|
||||
static final int INSET_SIDE_BOTTOM = 3;
|
||||
static final int INSET_SIDE_UNKNWON = 4;
|
||||
|
||||
private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>();
|
||||
|
||||
public InsetsState() {
|
||||
}
|
||||
|
||||
public InsetsState(InsetsState copy) {
|
||||
set(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates {@link WindowInsets} based on the current source configuration.
|
||||
*
|
||||
@@ -89,7 +111,8 @@ public class InsetsState implements Parcelable {
|
||||
* @return The calculated insets.
|
||||
*/
|
||||
public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
|
||||
boolean alwaysConsumeNavBar, DisplayCutout cutout) {
|
||||
boolean alwaysConsumeNavBar, DisplayCutout cutout,
|
||||
@Nullable @InsetSide SparseIntArray typeSideMap) {
|
||||
Insets systemInsets = Insets.NONE;
|
||||
Insets maxInsets = Insets.NONE;
|
||||
final Rect relativeFrame = new Rect(frame);
|
||||
@@ -100,13 +123,13 @@ public class InsetsState implements Parcelable {
|
||||
continue;
|
||||
}
|
||||
systemInsets = processSource(source, systemInsets, relativeFrame,
|
||||
false /* ignoreVisibility */);
|
||||
false /* ignoreVisibility */, typeSideMap);
|
||||
|
||||
// IME won't be reported in max insets as the size depends on the EditorInfo of the IME
|
||||
// target.
|
||||
if (source.getType() != TYPE_IME) {
|
||||
maxInsets = processSource(source, maxInsets, relativeFrameMax,
|
||||
true /* ignoreVisibility */);
|
||||
true /* ignoreVisibility */, null /* typeSideMap */);
|
||||
}
|
||||
}
|
||||
return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
|
||||
@@ -114,13 +137,39 @@ public class InsetsState implements Parcelable {
|
||||
}
|
||||
|
||||
private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
|
||||
boolean ignoreVisibility) {
|
||||
boolean ignoreVisibility, @Nullable @InsetSide SparseIntArray typeSideMap) {
|
||||
Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
|
||||
insets = Insets.add(currentInsets, insets);
|
||||
relativeFrame.inset(insets);
|
||||
if (typeSideMap != null && !Insets.NONE.equals(currentInsets)) {
|
||||
@InsetSide int insetSide = getInsetSide(currentInsets);
|
||||
if (insetSide != INSET_SIDE_UNKNWON) {
|
||||
typeSideMap.put(source.getType(), getInsetSide(currentInsets));
|
||||
}
|
||||
}
|
||||
return insets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b
|
||||
* is set in order that this method returns a meaningful result.
|
||||
*/
|
||||
private @InsetSide int getInsetSide(Insets insets) {
|
||||
if (insets.left != 0) {
|
||||
return INSET_SIDE_LEFT;
|
||||
}
|
||||
if (insets.top != 0) {
|
||||
return INSET_SIDE_TOP;
|
||||
}
|
||||
if (insets.right != 0) {
|
||||
return INSET_SIDE_RIGHT;
|
||||
}
|
||||
if (insets.bottom != 0) {
|
||||
return INSET_SIDE_BOTTOM;
|
||||
}
|
||||
return INSET_SIDE_UNKNWON;
|
||||
}
|
||||
|
||||
public InsetsSource getSource(@InternalInsetType int type) {
|
||||
return mSources.computeIfAbsent(type, InsetsSource::new);
|
||||
}
|
||||
|
||||
151
core/java/android/view/SyncRtSurfaceTransactionApplier.java
Normal file
151
core/java/android/view/SyncRtSurfaceTransactionApplier.java
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Helper class to apply surface transactions in sync with RenderThread.
|
||||
* @hide
|
||||
*/
|
||||
public class SyncRtSurfaceTransactionApplier {
|
||||
|
||||
private final Surface mTargetSurface;
|
||||
private final ViewRootImpl mTargetViewRootImpl;
|
||||
private final float[] mTmpFloat9 = new float[9];
|
||||
|
||||
/**
|
||||
* @param targetView The view in the surface that acts as synchronization anchor.
|
||||
*/
|
||||
public SyncRtSurfaceTransactionApplier(View targetView) {
|
||||
mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
|
||||
mTargetSurface = mTargetViewRootImpl != null ? mTargetViewRootImpl.mSurface : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules applying surface parameters on the next frame.
|
||||
*
|
||||
* @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
|
||||
* this method to avoid synchronization issues.
|
||||
*/
|
||||
public void scheduleApply(final SurfaceParams... params) {
|
||||
if (mTargetViewRootImpl == null) {
|
||||
return;
|
||||
}
|
||||
mTargetViewRootImpl.registerRtFrameCallback(frame -> {
|
||||
if (mTargetSurface == null || !mTargetSurface.isValid()) {
|
||||
return;
|
||||
}
|
||||
Transaction t = new Transaction();
|
||||
for (int i = params.length - 1; i >= 0; i--) {
|
||||
SurfaceParams surfaceParams = params[i];
|
||||
SurfaceControl surface = surfaceParams.surface;
|
||||
t.deferTransactionUntilSurface(surface, mTargetSurface, frame);
|
||||
applyParams(t, surfaceParams, mTmpFloat9);
|
||||
}
|
||||
t.setEarlyWakeup();
|
||||
t.apply();
|
||||
});
|
||||
|
||||
// Make sure a frame gets scheduled.
|
||||
mTargetViewRootImpl.getView().invalidate();
|
||||
}
|
||||
|
||||
public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
|
||||
t.setMatrix(params.surface, params.matrix, tmpFloat9);
|
||||
t.setWindowCrop(params.surface, params.windowCrop);
|
||||
t.setAlpha(params.surface, params.alpha);
|
||||
t.setLayer(params.surface, params.layer);
|
||||
t.setCornerRadius(params.surface, params.cornerRadius);
|
||||
t.show(params.surface);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
|
||||
* attached if necessary.
|
||||
*/
|
||||
public static void create(final View targetView,
|
||||
final Consumer<SyncRtSurfaceTransactionApplier> callback) {
|
||||
if (targetView == null) {
|
||||
// No target view, no applier
|
||||
callback.accept(null);
|
||||
} else if (targetView.getViewRootImpl() != null) {
|
||||
// Already attached, we're good to go
|
||||
callback.accept(new SyncRtSurfaceTransactionApplier(targetView));
|
||||
} else {
|
||||
// Haven't been attached before we can get the view root
|
||||
targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
targetView.removeOnAttachStateChangeListener(this);
|
||||
callback.accept(new SyncRtSurfaceTransactionApplier(targetView));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) {
|
||||
// Do nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class SurfaceParams {
|
||||
|
||||
/**
|
||||
* Constructs surface parameters to be applied when the current view state gets pushed to
|
||||
* RenderThread.
|
||||
*
|
||||
* @param surface The surface to modify.
|
||||
* @param alpha Alpha to apply.
|
||||
* @param matrix Matrix to apply.
|
||||
* @param windowCrop Crop to apply.
|
||||
*/
|
||||
public SurfaceParams(SurfaceControl surface, float alpha, Matrix matrix,
|
||||
Rect windowCrop, int layer, float cornerRadius) {
|
||||
this.surface = surface;
|
||||
this.alpha = alpha;
|
||||
this.matrix = new Matrix(matrix);
|
||||
this.windowCrop = new Rect(windowCrop);
|
||||
this.layer = layer;
|
||||
this.cornerRadius = cornerRadius;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public final SurfaceControl surface;
|
||||
|
||||
@VisibleForTesting
|
||||
public final float alpha;
|
||||
|
||||
@VisibleForTesting
|
||||
final float cornerRadius;
|
||||
|
||||
@VisibleForTesting
|
||||
public final Matrix matrix;
|
||||
|
||||
@VisibleForTesting
|
||||
public final Rect windowCrop;
|
||||
|
||||
@VisibleForTesting
|
||||
public final int layer;
|
||||
}
|
||||
}
|
||||
@@ -10489,6 +10489,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
*
|
||||
* @return The {@link WindowInsetsController} or {@code null} if the view isn't attached to a
|
||||
* a window.
|
||||
* @see Window#getInsetsController()
|
||||
* @hide pending unhide
|
||||
*/
|
||||
public @Nullable WindowInsetsController getWindowInsetsController() {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.annotation.NonNull;
|
||||
import android.view.WindowInsets.Type.InsetType;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
|
||||
/**
|
||||
* Interface that informs the client about {@link WindowInsetsAnimationController} state changes.
|
||||
* @hide pending unhide
|
||||
*/
|
||||
public interface WindowInsetsAnimationControlListener {
|
||||
|
||||
/**
|
||||
* Gets called as soon as the animation is ready to be controlled. This may be
|
||||
* delayed when the IME needs to redraw because of an {@link EditorInfo} change, or when the
|
||||
* window is starting up.
|
||||
*
|
||||
* @param controller The controller to control the inset animation.
|
||||
* @param types The {@link InsetType}s it was able to gain control over. Note that this may be
|
||||
* different than the types passed into
|
||||
* {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
|
||||
* wasn't able to gain the controls because it wasn't the IME target or not
|
||||
* currently the window that's controlling the system bars.
|
||||
*/
|
||||
void onReady(@NonNull WindowInsetsAnimationController controller, @InsetType int types);
|
||||
|
||||
/**
|
||||
* Called when the window no longer has control over the requested types. If it loses control
|
||||
* over one type, the whole control will be cancelled. If none of the requested types were
|
||||
* available when requesting the control, the animation control will be cancelled immediately
|
||||
* without {@link #onReady} being called.
|
||||
*/
|
||||
void onCancelled();
|
||||
}
|
||||
81
core/java/android/view/WindowInsetsAnimationController.java
Normal file
81
core/java/android/view/WindowInsetsAnimationController.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.annotation.NonNull;
|
||||
import android.graphics.Insets;
|
||||
import android.view.WindowInsets.Type.InsetType;
|
||||
|
||||
/**
|
||||
* Interface to control a window inset animation frame-by-frame.
|
||||
* @hide pending unhide
|
||||
*/
|
||||
public interface WindowInsetsAnimationController {
|
||||
|
||||
/**
|
||||
* Retrieves the {@link Insets} when the windows this animation is controlling are fully hidden.
|
||||
*
|
||||
* @return Insets when the windows this animation is controlling are fully hidden.
|
||||
*/
|
||||
@NonNull Insets getHiddenStateInsets();
|
||||
|
||||
/**
|
||||
* Retrieves the {@link Insets} when the windows this animation is controlling are fully shown.
|
||||
* <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.
|
||||
*
|
||||
* @return Insets when the windows this animation is controlling are fully shown.
|
||||
*/
|
||||
@NonNull Insets getShownStateInsets();
|
||||
|
||||
/**
|
||||
* @return The current insets on the window. These will follow any animation changes.
|
||||
*/
|
||||
@NonNull Insets getCurrentInsets();
|
||||
|
||||
/**
|
||||
* @return The {@link InsetType}s this object is currently controlling.
|
||||
*/
|
||||
@InsetType int getTypes();
|
||||
|
||||
/**
|
||||
* Modifies the insets by indirectly moving the windows around in the system that are causing
|
||||
* window insets.
|
||||
* <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.
|
||||
* <p>
|
||||
* {@link View#dispatchApplyWindowInsets} will instead be called once the animation has
|
||||
* finished, i.e. once {@link #finish} has been called.
|
||||
*
|
||||
* @param insets The new insets to apply. Based on the requested insets, the system will
|
||||
* calculate the positions of the windows in the system causing insets such that
|
||||
* 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}
|
||||
*/
|
||||
void changeInsets(@NonNull Insets insets);
|
||||
|
||||
/**
|
||||
* @param shownTypes The list of windows causing insets that should remain shown after finishing
|
||||
* the animation.
|
||||
*/
|
||||
void finish(@InsetType int shownTypes);
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.view.WindowInsets.Type.InsetType;
|
||||
|
||||
/**
|
||||
@@ -51,4 +52,15 @@ public interface WindowInsetsController {
|
||||
* would like to make disappear.
|
||||
*/
|
||||
void hide(@InsetType int types);
|
||||
|
||||
/**
|
||||
* Lets the application control window inset animations in a frame-by-frame manner by modifying
|
||||
* the position of the windows in the system causing insets directly.
|
||||
*
|
||||
* @param types The {@link InsetType}s the application has requested to control.
|
||||
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
|
||||
* windows are ready to be controlled, among other callbacks.
|
||||
*/
|
||||
void controlWindowInsetsAnimation(@InsetType int types,
|
||||
@NonNull WindowInsetsAnimationControlListener listener);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user