SurfaceControlViewHost: Expose transferTouchGesture API.

A key feature of SurfaceControlViewHost is that touches are given
directly to the embedded View hierarchy, however in some cases
this can interfere with the seamlessness of embedding. For example
if we are not clicking on an embedded element but in fact
intending to scroll its container.

Allowing transferring the current touch gesture to the host
provides a way to solve this problem. We expose this through
a SurfaceControlViewHost specific API in order to restrict scope
and avoid sharing of any new tokens.

Bug: 256993188
Change-Id: If94dbe4b862b6a159f2c0baee55dcb8c82c22e37
This commit is contained in:
Rob Carr
2022-10-10 23:15:49 +00:00
parent 0d09871867
commit 716ac1c2a9
5 changed files with 80 additions and 2 deletions

View File

@@ -353,4 +353,6 @@ interface IWindowSession {
* Returns whether this window needs to cancel draw and retry later.
*/
boolean cancelDraw(IWindow window);
boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow);
}

View File

@@ -194,7 +194,7 @@ public class SurfaceControlViewHost {
* is more akin to a PopupWindow in that the size is user specified
* independent of configuration width and height.
*
* In order to receive the configuration change via
* In order to receive the configuration change via
* {@link View#onConfigurationChanged}, the context used with the
* SurfaceControlViewHost and it's embedded view hierarchy must
* be a WindowContext obtained from {@link Context#createWindowContext}.
@@ -460,5 +460,26 @@ public class SurfaceControlViewHost {
(WindowManagerImpl) mViewRoot.mContext.getSystemService(Context.WINDOW_SERVICE);
attrs.token = wm.getDefaultToken();
}
}
/**
* Transfer the currently in progress touch gesture to the parent
* (if any) of this SurfaceControlViewHost. This requires that the
* SurfaceControlViewHost was created with an associated hostInputToken.
*
* @return Whether the touch stream was transferred.
* @hide
*/
public boolean transferTouchGestureToHost() {
if (mViewRoot == null) {
return false;
}
final IWindowSession realWm = WindowManagerGlobal.getWindowSession();
try {
return realWm.transferEmbeddedTouchFocusToHost(mViewRoot.mWindow);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
return false;
}
}

View File

@@ -561,4 +561,11 @@ public class WindowlessWindowManager implements IWindowSession {
public boolean cancelDraw(IWindow window) {
return false;
}
@Override
public boolean transferEmbeddedTouchFocusToHost(IWindow window) {
Log.e(TAG, "Received request to transferEmbeddedTouch focus on WindowlessWindowManager" +
" we shouldn't get here!");
return false;
}
}

View File

@@ -909,6 +909,22 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
}
@Override
public boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) {
if (embeddedWindow == null) {
return false;
}
final long identity = Binder.clearCallingIdentity();
boolean didTransfer = false;
try {
didTransfer = mService.transferEmbeddedTouchFocusToHost(embeddedWindow);
} finally {
Binder.restoreCallingIdentity(identity);
}
return didTransfer;
}
@Override
public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
RemoteCallback callback) {

View File

@@ -8669,6 +8669,38 @@ public class WindowManagerService extends IWindowManager.Stub
clientChannel.copyTo(outInputChannel);
}
boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) {
final IBinder windowBinder = embeddedWindow.asBinder();
final IBinder hostInputChannel, embeddedInputChannel;
synchronized (mGlobalLock) {
final EmbeddedWindowController.EmbeddedWindow ew =
mEmbeddedWindowController.getByWindowToken(windowBinder);
if (ew == null) {
Slog.w(TAG, "Attempt to transfer touch focus from non-existent embedded window");
return false;
}
final WindowState hostWindowState = ew.getWindowState();
if (hostWindowState == null) {
Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no" +
" associated host");
return false;
}
embeddedInputChannel = ew.getInputChannelToken();
if (embeddedInputChannel == null) {
Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no input" +
" channel");
return false;
}
hostInputChannel = hostWindowState.mInputChannelToken;
if (hostInputChannel == null) {
Slog.w(TAG, "Attempt to transfer touch focus to a host window with no" +
" input channel");
return false;
}
return mInputManager.transferTouchFocus(embeddedInputChannel, hostInputChannel);
}
}
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
int displayId, SurfaceControl surface, String name,
InputApplicationHandle applicationHandle, int flags,