Merge "Implement chip clipping without reparenting" into rvc-dev

This commit is contained in:
Svetoslav Ganov
2020-06-23 22:43:14 +00:00
committed by Android (Google) Code Review

View File

@@ -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);
} }
} }
} }