Merge "Reduce the window tap exclude region for child above it"

This commit is contained in:
Tiger Huang
2019-03-29 09:53:06 +00:00
committed by Android (Google) Code Review
12 changed files with 282 additions and 61 deletions

View File

@@ -27,6 +27,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Insets;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.hardware.input.InputManager;
@@ -46,6 +47,7 @@ import android.view.SurfaceSession;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -81,6 +83,9 @@ public class ActivityView extends ViewGroup {
// Temp container to store view coordinates in window.
private final int[] mLocationInWindow = new int[2];
// The latest tap exclude region that we've sent to WM.
private final Region mTapExcludeRegion = new Region();
private TaskStackListener mTaskStackListener;
private final CloseGuard mGuard = CloseGuard.get();
@@ -279,11 +284,11 @@ public class ActivityView extends ViewGroup {
}
/**
* Triggers an update of {@link ActivityView}'s location in window to properly set touch exclude
* Triggers an update of {@link ActivityView}'s location in window to properly set tap exclude
* regions and avoid focus switches by touches on this view.
*/
public void onLocationChanged() {
updateLocation();
updateTapExcludeRegion();
}
@Override
@@ -291,15 +296,38 @@ public class ActivityView extends ViewGroup {
mSurfaceView.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */);
}
/** Send current location and size to the WM to set tap exclude region for this view. */
private void updateLocation() {
@Override
public boolean gatherTransparentRegion(Region region) {
// The tap exclude region may be affected by any view on top of it, so we detect the
// possible change by monitoring this function.
updateTapExcludeRegion();
return super.gatherTransparentRegion(region);
}
/** Compute and send current tap exclude region to WM for this view. */
private void updateTapExcludeRegion() {
if (!isAttachedToWindow()) {
return;
}
if (!canReceivePointerEvents()) {
cleanTapExcludeRegion();
return;
}
try {
getLocationInWindow(mLocationInWindow);
final int x = mLocationInWindow[0];
final int y = mLocationInWindow[1];
mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight());
// There might be views on top of us. We need to subtract those areas from the tap
// exclude region.
final ViewParent parent = getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).subtractObscuredTouchableRegion(mTapExcludeRegion, this);
}
WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
mLocationInWindow[0], mLocationInWindow[1], getWidth(), getHeight());
mTapExcludeRegion);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
@@ -322,7 +350,7 @@ public class ActivityView extends ViewGroup {
mVirtualDisplay.setDisplayState(true);
}
updateLocation();
updateTapExcludeRegion();
}
@Override
@@ -330,7 +358,7 @@ public class ActivityView extends ViewGroup {
if (mVirtualDisplay != null) {
mVirtualDisplay.resize(width, height, getBaseDisplayDensity());
}
updateLocation();
updateTapExcludeRegion();
}
@Override
@@ -460,13 +488,14 @@ public class ActivityView extends ViewGroup {
/** Report to server that tap exclude region on hosting display should be cleared. */
private void cleanTapExcludeRegion() {
if (!isAttachedToWindow()) {
if (!isAttachedToWindow() || mTapExcludeRegion.isEmpty()) {
return;
}
// Update tap exclude region with an empty rect to clean the state on server.
// Update tap exclude region with a null region to clean the state on server.
try {
WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
0 /* left */, 0 /* top */, 0 /* width */, 0 /* height */);
null /* region */);
mTapExcludeRegion.setEmpty();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}

View File

@@ -255,12 +255,11 @@ interface IWindowSession {
void updatePointerIcon(IWindow window);
/**
* Update a tap exclude region with a rectangular area identified by provided id in the window.
* Touches on this region will not switch focus to this window. Passing an empty rect will
* remove the area from the exclude region of this window.
* Update a tap exclude region identified by provided id in the window. Touches on this region
* will neither be dispatched to this window nor change the focus to this window. Passing an
* invalid region will remove the area from the exclude region of this window.
*/
void updateTapExcludeRegion(IWindow window, int regionId, int left, int top, int width,
int height);
void updateTapExcludeRegion(IWindow window, int regionId, in Region region);
/**
* Called when the client has changed the local insets state, and now the server should reflect

View File

@@ -13910,6 +13910,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
.getAccessibilityFocusedHost() == this);
}
/**
* Returns whether this view can receive pointer events.
*
* @return {@code true} if this view can receive pointer events.
* @hide
*/
protected boolean canReceivePointerEvents() {
return (mViewFlags & VISIBILITY_MASK) == VISIBLE || getAnimation() != null;
}
/**
* Filter the touch event to apply security policies.
*

View File

@@ -2011,7 +2011,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if (!canViewReceivePointerEvents(child)
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
@@ -2094,7 +2094,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
if (!canViewReceivePointerEvents(child)
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
@@ -2314,7 +2314,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child =
getAndVerifyPreorderedView(preorderedList, children, childIndex);
if (!canViewReceivePointerEvents(child)
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
@@ -2500,7 +2500,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if (!canViewReceivePointerEvents(child)
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
@@ -2680,7 +2680,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
i = childrenCount - 1;
}
if (!canViewReceivePointerEvents(child)
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
@@ -2970,15 +2970,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
/**
* Returns true if a child view can receive pointer events.
* @hide
*/
private static boolean canViewReceivePointerEvents(@NonNull View child) {
return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null;
}
private float[] getTempPoint() {
if (mTempPoint == null) {
mTempPoint = new float[2];
@@ -7199,6 +7190,46 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
/**
* @hide
*/
@Override
public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
final int childrenCount = mChildrenCount;
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if (child == view) {
// We've reached the target view.
break;
}
if (!child.canReceivePointerEvents()) {
// This child cannot be touched. Skip it.
continue;
}
applyOpToRegionByBounds(touchableRegion, child, Region.Op.DIFFERENCE);
}
// The touchable region should not exceed the bounds of its container.
applyOpToRegionByBounds(touchableRegion, this, Region.Op.INTERSECT);
final ViewParent parent = getParent();
if (parent != null) {
parent.subtractObscuredTouchableRegion(touchableRegion, this);
}
}
private static void applyOpToRegionByBounds(Region region, View view, Region.Op op) {
final int[] locationInWindow = new int[2];
view.getLocationInWindow(locationInWindow);
final int x = locationInWindow[0];
final int y = locationInWindow[1];
region.op(x, y, x + view.getWidth(), y + view.getHeight(), op);
}
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
insets = super.dispatchApplyWindowInsets(insets);

View File

@@ -18,6 +18,7 @@ package android.view;
import android.annotation.NonNull;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.view.accessibility.AccessibilityEvent;
@@ -660,4 +661,17 @@ public interface ViewParent {
* @return true if the action was consumed by this ViewParent
*/
public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle arguments);
/**
* Given a touchable region of a child, this method reduces region by the bounds of all views on
* top of the child for which {@link View#canReceivePointerEvents} returns {@code true}. This
* applies recursively for all views in the view hierarchy on top of this one.
*
* @param touchableRegion The touchable region we want to modify.
* @param view A child view of this ViewGroup which indicates the z-order of the touchable
* region.
* @hide
*/
default void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
}
}