Merge "Let the activity embedded in ActivityView can be directly touched"

This commit is contained in:
Tiger Huang
2019-01-30 10:36:02 +00:00
committed by Android (Google) Code Review
8 changed files with 153 additions and 67 deletions

View File

@@ -28,15 +28,12 @@ import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.hardware.input.InputManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceSession;
@@ -51,9 +48,7 @@ import dalvik.system.CloseGuard;
import java.util.List;
/**
* Activity container that allows launching activities into itself and does input forwarding.
* <p>Creation of this view is only allowed to callers who have
* {@link android.Manifest.permission#INJECT_EVENTS} permission.
* Activity container that allows launching activities into itself.
* <p>Activity launching into this container is restricted by the same rules that apply to launching
* on VirtualDisplays.
* @hide
@@ -76,9 +71,8 @@ public class ActivityView extends ViewGroup {
private StateCallback mActivityViewCallback;
private IActivityTaskManager mActivityTaskManager;
private IInputForwarder mInputForwarder;
// Temp container to store view coordinates on screen.
private final int[] mLocationOnScreen = new int[2];
// Temp container to store view coordinates in window.
private final int[] mLocationInWindow = new int[2];
private TaskStackListener mTaskStackListener;
@@ -280,7 +274,7 @@ public class ActivityView extends ViewGroup {
}
/**
* Triggers an update of {@link ActivityView}'s location on screen to properly set touch exclude
* Triggers an update of {@link ActivityView}'s location in window to properly set touch exclude
* regions and avoid focus switches by touches on this view.
*/
public void onLocationChanged() {
@@ -295,45 +289,14 @@ public class ActivityView extends ViewGroup {
/** Send current location and size to the WM to set tap exclude region for this view. */
private void updateLocation() {
try {
getLocationOnScreen(mLocationOnScreen);
getLocationInWindow(mLocationInWindow);
WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(),
mLocationOnScreen[0], mLocationOnScreen[1], getWidth(), getHeight());
mLocationInWindow[0], mLocationInWindow[1], getWidth(), getHeight());
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return injectInputEvent(event) || super.onTouchEvent(event);
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
if (injectInputEvent(event)) {
return true;
}
}
return super.onGenericMotionEvent(event);
}
private boolean injectInputEvent(MotionEvent event) {
if (mInputForwarder != null) {
try {
// The touch event that the ActivityView gets is in View space, but the event needs
// to get forwarded in screen space. This offsets the touch event by the location
// the ActivityView is on screen and sends it to the input forwarder.
getLocationOnScreen(mLocationOnScreen);
event.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
return mInputForwarder.forwardEvent(event);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
return false;
}
private class SurfaceCallback implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
@@ -416,7 +379,6 @@ public class ActivityView extends ViewGroup {
}
mTmpTransaction.show(mRootSurfaceControl).apply();
mInputForwarder = InputManager.getInstance().createInputForwarder(displayId);
mTaskStackListener = new TaskStackListenerImpl();
try {
mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
@@ -432,9 +394,6 @@ public class ActivityView extends ViewGroup {
mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
if (mInputForwarder != null) {
mInputForwarder = null;
}
cleanTapExcludeRegion();
if (mTaskStackListener != null) {

View File

@@ -16,10 +16,10 @@
package android.view;
import static android.view.Display.INVALID_DISPLAY;
import android.graphics.Region;
import android.os.IBinder;
import android.view.IWindow;
import android.view.InputChannel;
/**
* Functions as a handle for a window that can receive input.
@@ -94,6 +94,10 @@ public final class InputWindowHandle {
// Display this input is on.
public int displayId;
// If this value is set to a valid display ID, it indicates this window is a portal which
// transports the touch of this window to the display indicated by portalToDisplayId.
public int portalToDisplayId = INVALID_DISPLAY;
private native void nativeDispose();
public InputWindowHandle(InputApplicationHandle inputApplicationHandle,

View File

@@ -55,6 +55,7 @@ static struct {
jfieldID ownerUid;
jfieldID inputFeatures;
jfieldID displayId;
jfieldID portalToDisplayId;
} gInputWindowHandleClassInfo;
static Mutex gHandleMutex;
@@ -154,6 +155,8 @@ bool NativeInputWindowHandle::updateInfo() {
gInputWindowHandleClassInfo.inputFeatures);
mInfo.displayId = env->GetIntField(obj,
gInputWindowHandleClassInfo.displayId);
mInfo.portalToDisplayId = env->GetIntField(obj,
gInputWindowHandleClassInfo.portalToDisplayId);
jobject inputApplicationHandleObj = env->GetObjectField(obj,
gInputWindowHandleClassInfo.inputApplicationHandle);
@@ -307,6 +310,9 @@ int register_android_view_InputWindowHandle(JNIEnv* env) {
GET_FIELD_ID(gInputWindowHandleClassInfo.displayId, clazz,
"displayId", "I");
GET_FIELD_ID(gInputWindowHandleClassInfo.portalToDisplayId, clazz,
"portalToDisplayId", "I");
return 0;
}

View File

@@ -29,6 +29,7 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InsetsState.TYPE_IME;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
@@ -43,6 +44,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE;
import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
@@ -140,9 +142,11 @@ import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.Region.Op;
import android.hardware.display.DisplayManagerInternal;
import android.os.Binder;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -157,6 +161,7 @@ import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputWindowHandle;
import android.view.InsetsState.InternalInsetType;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationDefinition;
@@ -515,6 +520,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private final InsetsStateController mInsetsStateController;
private SurfaceControl mParentSurfaceControl;
private InputWindowHandle mPortalWindowHandle;
// Last systemUiVisibility we received from status bar.
private int mLastStatusBarVisibility = 0;
// Last systemUiVisibility we dispatched to windows.
@@ -2410,10 +2418,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
win.getTouchableRegion(mTmpRegion);
mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION);
}
for (int i = mTapExcludeProvidingWindows.size() - 1; i >= 0; i--) {
final WindowState win = mTapExcludeProvidingWindows.valueAt(i);
win.amendTapExcludeRegion(mTouchExcludeRegion);
}
amendWindowTapExcludeRegion(mTouchExcludeRegion);
// TODO(multi-display): Support docked stacks on secondary displays.
if (mDisplayId == DEFAULT_DISPLAY && getSplitScreenPrimaryStack() != null) {
mDividerControllerLocked.getTouchRegion(mTmpRect);
@@ -2425,6 +2430,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
/**
* Union the region with all the tap exclude region provided by windows on this display.
*
* @param inOutRegion The region to be amended.
*/
void amendWindowTapExcludeRegion(Region inOutRegion) {
for (int i = mTapExcludeProvidingWindows.size() - 1; i >= 0; i--) {
final WindowState win = mTapExcludeProvidingWindows.valueAt(i);
win.amendTapExcludeRegion(inOutRegion);
}
}
@Override
void switchUser() {
super.switchUser();
@@ -3586,6 +3603,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private void updateBounds() {
calculateBounds(mDisplayInfo, mTmpBounds);
setBounds(mTmpBounds);
if (mPortalWindowHandle != null && mParentSurfaceControl != null) {
mPortalWindowHandle.touchableRegion.getBounds(mTmpRect);
if (!mTmpBounds.equals(mTmpRect)) {
mPortalWindowHandle.touchableRegion.set(mTmpBounds);
mPendingTransaction.setInputWindowInfo(mParentSurfaceControl, mPortalWindowHandle);
}
}
}
// Determines the current display bounds based on the current state
@@ -4830,15 +4854,43 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|| mWmService.mForceDesktopModeOnExternalDisplays;
}
/**
/**
* Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
* {@link #mOverlayLayer} to the specified surfaceControl.
*
* @param surfaceControlHandle The new SurfaceControl, where the DisplayContent's
* surfaces will be re-parented to.
* @param sc The new SurfaceControl, where the DisplayContent's surfaces will be re-parented to.
*/
void reparentDisplayContent(SurfaceControl sc) {
mPendingTransaction.reparent(mWindowingLayer, sc)
.reparent(mOverlayLayer, sc);
mParentSurfaceControl = sc;
if (mPortalWindowHandle == null) {
mPortalWindowHandle = createPortalWindowHandle(sc.toString());
}
mPendingTransaction.setInputWindowInfo(sc, mPortalWindowHandle)
.reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc);
}
/**
* Create a portal window handle for input. This window transports any touch to the display
* indicated by {@link InputWindowHandle#portalToDisplayId} if the touch hits this window.
*
* @param name The name of the portal window handle.
* @return the new portal window handle.
*/
private InputWindowHandle createPortalWindowHandle(String name) {
// Let surface flinger to set the display ID of this input window handle because we don't
// know which display the parent surface control is on.
final InputWindowHandle portalWindowHandle = new InputWindowHandle(
null /* inputApplicationHandle */, null /* clientWindow */, INVALID_DISPLAY);
portalWindowHandle.name = name;
portalWindowHandle.token = new Binder();
portalWindowHandle.layoutParamsFlags =
FLAG_SPLIT_TOUCH | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL;
getBounds(mTmpBounds);
portalWindowHandle.touchableRegion.set(mTmpBounds);
portalWindowHandle.scaleFactor = 1f;
portalWindowHandle.ownerPid = Process.myPid();
portalWindowHandle.ownerUid = Process.myUid();
portalWindowHandle.portalToDisplayId = mDisplayId;
return portalWindowHandle;
}
}

View File

@@ -49,7 +49,9 @@ class TapExcludeRegionHolder {
void amendRegion(Region region, Rect boundingRegion) {
for (int i = mTapExcludeRects.size() - 1; i>= 0 ; --i) {
final Rect rect = mTapExcludeRects.valueAt(i);
rect.intersect(boundingRegion);
if (boundingRegion != null) {
rect.intersect(boundingRegion);
}
region.union(rect);
}
}

View File

@@ -70,6 +70,18 @@ public class TaskTapPointerEventListener implements PointerEventListener {
// method target window will lose the focus.
return;
}
final Region windowTapExcludeRegion = Region.obtain();
mDisplayContent.amendWindowTapExcludeRegion(windowTapExcludeRegion);
if (windowTapExcludeRegion.contains(x, y)) {
windowTapExcludeRegion.recycle();
// The user is tapping on the window tap exclude region. We don't move this
// display to top. A window tap exclude region, for example, may be set by an
// ActivityView, and the region would match the bounds of both the ActivityView
// and the virtual display in it. In this case, we would take the tap that is on
// the embedded virtual display instead of this display.
return;
}
windowTapExcludeRegion.recycle();
WindowContainer parent = mDisplayContent.getParent();
if (parent != null && parent.getTopChild() != mDisplayContent) {
parent.positionChildAt(WindowContainer.POSITION_TOP, mDisplayContent,
@@ -81,9 +93,6 @@ public class TaskTapPointerEventListener implements PointerEventListener {
@Override
public void onPointerEvent(MotionEvent motionEvent) {
if (motionEvent.getDisplayId() != getDisplayId()) {
return;
}
switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
final int x = (int) motionEvent.getX();

View File

@@ -6532,8 +6532,13 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Update a tap exclude region with a rectangular area in the window identified by the provided
* id. 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.
* id. Touches down on this region will not:
* <ol>
* <li>Switch focus to this window.</li>
* <li>Move the display of this window to top.</li>
* <li>Send the touch events to this window.</li>
* </ol>
* Passing an empty rect will remove the area from the exclude region of this window.
*/
void updateTapExcludeRegion(IWindow client, int regionId, int left, int top, int width,
int height) {

View File

@@ -536,7 +536,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private final Point mSurfacePosition = new Point();
/**
* A region inside of this window to be excluded from touch-related focus switches.
* A region inside of this window to be excluded from touch.
*/
private TapExcludeRegionHolder mTapExcludeRegionHolder;
@@ -2168,6 +2168,24 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
region.set(mTmpRect);
cropRegionToStackBoundsIfNeeded(region);
subtractTouchExcludeRegionIfNeeded(region);
} else if (modal && mTapExcludeRegionHolder != null) {
final Region touchExcludeRegion = Region.obtain();
amendTapExcludeRegion(touchExcludeRegion);
if (!touchExcludeRegion.isEmpty()) {
// Remove touch modal because there are some areas that cannot be touched.
flags |= FLAG_NOT_TOUCH_MODAL;
// Give it a large touchable region at first because it was touch modal. The window
// might be moved on the display, so the touchable region should be large enough to
// ensure it covers the whole display, no matter where it is moved.
getDisplayContent().getBounds(mTmpRect);
final int dw = mTmpRect.width();
final int dh = mTmpRect.height();
region.set(-dw, -dh, dw + dw, dh + dh);
// Subtract the area that cannot be touched.
region.op(touchExcludeRegion, Region.Op.DIFFERENCE);
}
touchExcludeRegion.recycle();
} else {
// Not modal or full screen modal
getTouchableRegion(region);
@@ -2837,6 +2855,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
cropRegionToStackBoundsIfNeeded(outRegion);
subtractTouchExcludeRegionIfNeeded(outRegion);
}
private void cropRegionToStackBoundsIfNeeded(Region region) {
@@ -2854,6 +2873,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
region.op(mTmpRect, Region.Op.INTERSECT);
}
/**
* If this window has areas that cannot be touched, we subtract those areas from its touchable
* region.
*/
private void subtractTouchExcludeRegionIfNeeded(Region touchableRegion) {
if (mTapExcludeRegionHolder == null) {
return;
}
final Region touchExcludeRegion = Region.obtain();
amendTapExcludeRegion(touchExcludeRegion);
if (!touchExcludeRegion.isEmpty()) {
touchableRegion.op(touchExcludeRegion, Region.Op.DIFFERENCE);
}
touchExcludeRegion.recycle();
}
/**
* Report a focus change. Must be called with no locks held, and consistently
* from the same serialized thread (such as dispatched from a handler).
@@ -4728,11 +4763,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mTapExcludeRegionHolder.updateRegion(regionId, left, top, width, height);
// Trigger touch exclude region update on current display.
currentDisplay.updateTouchExcludeRegion();
// Trigger touchable region update for this window.
currentDisplay.getInputMonitor().updateInputWindowsLw(true /* force */);
}
/** Union the region with current tap exclude region that this window provides. */
/**
* Union the region with current tap exclude region that this window provides.
*
* @param region The region to be amended. It is on the screen coordinates.
*/
void amendTapExcludeRegion(Region region) {
mTapExcludeRegionHolder.amendRegion(region, getBounds());
final Region tempRegion = Region.obtain();
mTmpRect.set(mWindowFrames.mFrame);
mTmpRect.offsetTo(0, 0);
mTapExcludeRegionHolder.amendRegion(tempRegion, mTmpRect);
// The region held by the holder is on the window coordinates. We need to translate it to
// the screen coordinates.
tempRegion.translate(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top);
region.op(tempRegion, Region.Op.UNION);
tempRegion.recycle();
}
@Override