Merge "Implement chip clipping without reparenting" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
bbba16a2c9
@@ -19,15 +19,16 @@ package com.example.android.autofillkeyboard;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.Choreographer;
|
import android.view.Choreographer;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.SurfaceControl;
|
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.inline.InlineContentView;
|
import android.widget.inline.InlineContentView;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
@@ -46,21 +47,21 @@ import androidx.collection.ArraySet;
|
|||||||
*/
|
*/
|
||||||
@RequiresApi(api = Build.VERSION_CODES.R)
|
@RequiresApi(api = Build.VERSION_CODES.R)
|
||||||
public class InlineContentClipView extends FrameLayout {
|
public class InlineContentClipView extends FrameLayout {
|
||||||
// The trick that we use here is to have a hidden SurfaceView to whose
|
@NonNull
|
||||||
// surface we reparent the surfaces of remote content views which are
|
private final ArraySet<InlineContentView> mClippedDescendants = new ArraySet<>();
|
||||||
// InlineContentViews. Since surface locations are based off the window
|
|
||||||
// top-left making, making one surface parent of another compounds the
|
|
||||||
// offset from the child's point of view. To compensate for that we
|
|
||||||
// apply transformation to the InlineContentViews.
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final ArraySet<InlineContentView> mReparentedDescendants = new ArraySet<>();
|
private final ViewTreeObserver.OnDrawListener mOnDrawListener =
|
||||||
|
this::clipDescendantInlineContentViews;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final int[] mTempLocation = new int[2];
|
private final Rect mParentBounds = new Rect();
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
SurfaceView mSurfaceClipView;
|
private final Rect mContentBounds = new Rect();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private SurfaceView mBackgroundView;
|
||||||
|
|
||||||
private int mBackgroundColor;
|
private int mBackgroundColor;
|
||||||
|
|
||||||
@@ -76,13 +77,13 @@ public class InlineContentClipView extends FrameLayout {
|
|||||||
@AttrRes int defStyleAttr) {
|
@AttrRes int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
|
|
||||||
mSurfaceClipView = new SurfaceView(context);
|
mBackgroundView = new SurfaceView(context);
|
||||||
mSurfaceClipView.setZOrderOnTop(true);
|
mBackgroundView.setZOrderOnTop(true);
|
||||||
mSurfaceClipView.getHolder().setFormat(PixelFormat.TRANSPARENT);
|
mBackgroundView.getHolder().setFormat(PixelFormat.TRANSPARENT);
|
||||||
mSurfaceClipView.setLayoutParams(new ViewGroup.LayoutParams(
|
mBackgroundView.setLayoutParams(new ViewGroup.LayoutParams(
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
mSurfaceClipView.getHolder().addCallback(new SurfaceHolder.Callback() {
|
mBackgroundView.getHolder().addCallback(new SurfaceHolder.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void surfaceCreated(@NonNull SurfaceHolder holder) {
|
public void surfaceCreated(@NonNull SurfaceHolder holder) {
|
||||||
drawBackgroundColorIfReady();
|
drawBackgroundColorIfReady();
|
||||||
@@ -94,18 +95,23 @@ public class InlineContentClipView extends FrameLayout {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
|
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
|
||||||
updateState(InlineContentClipView.this, /*parentSurfaceProvider*/ null);
|
/*do nothing*/
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addView(mSurfaceClipView);
|
addView(mBackgroundView);
|
||||||
|
}
|
||||||
|
|
||||||
setWillNotDraw(false);
|
@Override
|
||||||
|
protected void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
getViewTreeObserver().addOnDrawListener(mOnDrawListener);
|
||||||
|
}
|
||||||
|
|
||||||
getViewTreeObserver().addOnPreDrawListener(() -> {
|
@Override
|
||||||
updateState(InlineContentClipView.this, mSurfaceClipView);
|
protected void onDetachedFromWindow() {
|
||||||
return true;
|
super.onDetachedFromWindow();
|
||||||
});
|
getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -116,7 +122,7 @@ public class InlineContentClipView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void drawBackgroundColorIfReady() {
|
private void drawBackgroundColorIfReady() {
|
||||||
final Surface surface = mSurfaceClipView.getHolder().getSurface();
|
final Surface surface = mBackgroundView.getHolder().getSurface();
|
||||||
if (surface.isValid()) {
|
if (surface.isValid()) {
|
||||||
final Canvas canvas = surface.lockCanvas(null);
|
final Canvas canvas = surface.lockCanvas(null);
|
||||||
try {
|
try {
|
||||||
@@ -140,79 +146,30 @@ public class InlineContentClipView extends FrameLayout {
|
|||||||
* @see InlineContentView#setZOrderedOnTop(boolean)
|
* @see InlineContentView#setZOrderedOnTop(boolean)
|
||||||
*/
|
*/
|
||||||
public void setZOrderedOnTop(boolean onTop) {
|
public void setZOrderedOnTop(boolean onTop) {
|
||||||
mSurfaceClipView.setZOrderOnTop(onTop);
|
mBackgroundView.setZOrderOnTop(onTop);
|
||||||
for (InlineContentView inlineContentView : mReparentedDescendants) {
|
for (InlineContentView inlineContentView : mClippedDescendants) {
|
||||||
inlineContentView.setZOrderedOnTop(onTop);
|
inlineContentView.setZOrderedOnTop(onTop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateState(@NonNull View root,
|
private void clipDescendantInlineContentViews() {
|
||||||
@Nullable SurfaceView parentSurfaceProvider) {
|
mParentBounds.right = getWidth();
|
||||||
if (parentSurfaceProvider != null) {
|
mParentBounds.bottom = getHeight();
|
||||||
mSurfaceClipView.getLocationInWindow(mTempLocation);
|
mClippedDescendants.clear();
|
||||||
} else {
|
clipDescendantInlineContentViews(this);
|
||||||
mTempLocation[0] = 0;
|
|
||||||
mTempLocation[1] = 0;
|
|
||||||
}
|
|
||||||
reparentChildSurfaceViewSurfacesRecursive(root, parentSurfaceProvider,
|
|
||||||
/*parentSurfaceLeft*/ mTempLocation[0], /*parentSurfaceTop*/ mTempLocation[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reparentChildSurfaceViewSurfacesRecursive(@Nullable View root,
|
private void clipDescendantInlineContentViews(@Nullable View root) {
|
||||||
@Nullable SurfaceView parentSurfaceProvider, int parentSurfaceLeft,
|
if (root == null) {
|
||||||
int parentSurfaceTop) {
|
|
||||||
if (root == null || root == mSurfaceClipView) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (root instanceof InlineContentView) {
|
if (root instanceof InlineContentView) {
|
||||||
// Surfaces of a surface view have a transformation matrix relative
|
|
||||||
// to the top-left of the window and when one is reparented to the
|
|
||||||
// other the transformation adds up and we need to compensate.
|
|
||||||
root.setTranslationX(-parentSurfaceLeft);
|
|
||||||
root.setTranslationY(-parentSurfaceTop);
|
|
||||||
|
|
||||||
final InlineContentView inlineContentView = (InlineContentView) root;
|
final InlineContentView inlineContentView = (InlineContentView) root;
|
||||||
if (parentSurfaceProvider != null) {
|
mContentBounds.set(mParentBounds);
|
||||||
if (mReparentedDescendants.contains(inlineContentView)) {
|
offsetRectIntoDescendantCoords(inlineContentView, mContentBounds);
|
||||||
return;
|
inlineContentView.setClipBounds(mContentBounds);
|
||||||
}
|
mClippedDescendants.add(inlineContentView);
|
||||||
|
|
||||||
inlineContentView.setSurfaceControlCallback(
|
|
||||||
new InlineContentView.SurfaceControlCallback() {
|
|
||||||
@Override
|
|
||||||
public void onCreated(SurfaceControl surfaceControl) {
|
|
||||||
// Our surface and its descendants are initially hidden until
|
|
||||||
// the descendants are reparented and their containers scrolled.
|
|
||||||
new SurfaceControl.Transaction()
|
|
||||||
.reparent(surfaceControl, parentSurfaceProvider.getSurfaceControl())
|
|
||||||
.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyed(SurfaceControl surfaceControl) {
|
|
||||||
/* do nothing */
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mReparentedDescendants.add(inlineContentView);
|
|
||||||
} else {
|
|
||||||
if (!mReparentedDescendants.contains(inlineContentView)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unparent the surface control of the removed surface view.
|
|
||||||
final SurfaceControl surfaceControl = inlineContentView.getSurfaceControl();
|
|
||||||
if (surfaceControl != null && surfaceControl.isValid()) {
|
|
||||||
new SurfaceControl.Transaction()
|
|
||||||
.reparent(surfaceControl, /*newParent*/ null)
|
|
||||||
.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
mReparentedDescendants.remove(inlineContentView);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,8 +178,7 @@ public class InlineContentClipView extends FrameLayout {
|
|||||||
final int childCount = rootGroup.getChildCount();
|
final int childCount = rootGroup.getChildCount();
|
||||||
for (int i = 0; i < childCount; i++) {
|
for (int i = 0; i < childCount; i++) {
|
||||||
final View child = rootGroup.getChildAt(i);
|
final View child = rootGroup.getChildAt(i);
|
||||||
reparentChildSurfaceViewSurfacesRecursive(child, parentSurfaceProvider,
|
clipDescendantInlineContentViews(child);
|
||||||
parentSurfaceLeft, parentSurfaceTop);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user