Loading core/java/android/app/ActivityView.java +39 −10 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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 Loading @@ -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(); } Loading @@ -322,7 +350,7 @@ public class ActivityView extends ViewGroup { mVirtualDisplay.setDisplayState(true); } updateLocation(); updateTapExcludeRegion(); } @Override Loading @@ -330,7 +358,7 @@ public class ActivityView extends ViewGroup { if (mVirtualDisplay != null) { mVirtualDisplay.resize(width, height, getBaseDisplayDensity()); } updateLocation(); updateTapExcludeRegion(); } @Override Loading Loading @@ -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(); } Loading core/java/android/view/IWindowSession.aidl +4 −5 Original line number Diff line number Diff line Loading @@ -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 Loading core/java/android/view/View.java +10 −0 Original line number Diff line number Diff line Loading @@ -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. * core/java/android/view/ViewGroup.java +45 −14 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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]; Loading Loading @@ -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); Loading core/java/android/view/ViewParent.java +14 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { } } Loading
core/java/android/app/ActivityView.java +39 −10 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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 Loading @@ -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(); } Loading @@ -322,7 +350,7 @@ public class ActivityView extends ViewGroup { mVirtualDisplay.setDisplayState(true); } updateLocation(); updateTapExcludeRegion(); } @Override Loading @@ -330,7 +358,7 @@ public class ActivityView extends ViewGroup { if (mVirtualDisplay != null) { mVirtualDisplay.resize(width, height, getBaseDisplayDensity()); } updateLocation(); updateTapExcludeRegion(); } @Override Loading Loading @@ -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(); } Loading
core/java/android/view/IWindowSession.aidl +4 −5 Original line number Diff line number Diff line Loading @@ -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 Loading
core/java/android/view/View.java +10 −0 Original line number Diff line number Diff line Loading @@ -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. *
core/java/android/view/ViewGroup.java +45 −14 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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]; Loading Loading @@ -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); Loading
core/java/android/view/ViewParent.java +14 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { } }