Merge "Let view focus move across adjacent task fragments" into main
This commit is contained in:
commit
6c202d41c8
@ -370,4 +370,14 @@ interface IWindowSession {
|
||||
boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow);
|
||||
|
||||
boolean transferHostTouchGestureToEmbedded(IWindow hostWindow, IBinder transferTouchToken);
|
||||
|
||||
/**
|
||||
* Moves the focus to the adjacent window if there is one in the given direction. This can only
|
||||
* move the focus to the window in the same leaf task.
|
||||
*
|
||||
* @param fromWindow The calling window that the focus is moved from.
|
||||
* @param direction The {@link android.view.View.FocusDirection} that the new focus should go.
|
||||
* @return {@code true} if the focus changes. Otherwise, {@code false}.
|
||||
*/
|
||||
boolean moveFocusToAdjacentWindow(IWindow fromWindow, int direction);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
import static android.content.pm.ActivityInfo.OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS;
|
||||
import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
|
||||
import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
|
||||
@ -7236,7 +7237,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
}
|
||||
|
||||
private boolean performFocusNavigation(KeyEvent event) {
|
||||
int direction = 0;
|
||||
@FocusDirection int direction = 0;
|
||||
switch (event.getKeyCode()) {
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
if (event.hasNoModifiers()) {
|
||||
@ -7288,6 +7289,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
isFastScrolling));
|
||||
return true;
|
||||
}
|
||||
} else if (moveFocusToAdjacentWindow(direction)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Give the focused view a last chance to handle the dpad key.
|
||||
@ -7297,12 +7300,26 @@ public final class ViewRootImpl implements ViewParent,
|
||||
} else {
|
||||
if (mView.restoreDefaultFocus()) {
|
||||
return true;
|
||||
} else if (moveFocusToAdjacentWindow(direction)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean moveFocusToAdjacentWindow(@FocusDirection int direction) {
|
||||
if (getConfiguration().windowConfiguration.getWindowingMode()
|
||||
!= WINDOWING_MODE_MULTI_WINDOW) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return mWindowSession.moveFocusToAdjacentWindow(mWindow, direction);
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean performKeyboardGroupNavigation(int direction) {
|
||||
final View focused = mView.findFocus();
|
||||
if (focused == null && mView.restoreDefaultFocus()) {
|
||||
|
@ -30,6 +30,7 @@ import android.os.RemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.MergedConfiguration;
|
||||
import android.view.View.FocusDirection;
|
||||
import android.view.WindowInsets.Type.InsetsType;
|
||||
import android.window.ClientWindowFrames;
|
||||
import android.window.OnBackInvokedCallbackInfo;
|
||||
@ -665,6 +666,13 @@ public class WindowlessWindowManager implements IWindowSession {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) {
|
||||
Log.e(TAG, "Received request to moveFocusToAdjacentWindow on"
|
||||
+ " WindowlessWindowManager. We shouldn't get here!");
|
||||
return false;
|
||||
}
|
||||
|
||||
void setParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) {
|
||||
IBinder oldInterface = mParentInterface == null ? null : mParentInterface.asBinder();
|
||||
IBinder newInterface = parentInterface == null ? null : parentInterface.asBinder();
|
||||
|
@ -75,6 +75,7 @@ import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.View;
|
||||
import android.view.View.FocusDirection;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsets.Type.InsetsType;
|
||||
import android.view.WindowManager;
|
||||
@ -1000,6 +1001,17 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
|
||||
}
|
||||
return didTransfer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) {
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return mService.moveFocusToAdjacentWindow(this, fromWindow, direction);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
|
||||
RemoteCallback callback) {
|
||||
|
@ -287,6 +287,7 @@ import android.view.SurfaceControlViewHost;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.TaskTransitionSpec;
|
||||
import android.view.View;
|
||||
import android.view.View.FocusDirection;
|
||||
import android.view.ViewDebug;
|
||||
import android.view.WindowContentFrameStats;
|
||||
import android.view.WindowInsets;
|
||||
@ -9104,6 +9105,66 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
win.mClient);
|
||||
}
|
||||
|
||||
boolean moveFocusToAdjacentWindow(Session session, IWindow fromWindow,
|
||||
@FocusDirection int direction) {
|
||||
synchronized (mGlobalLock) {
|
||||
final WindowState fromWin = windowForClientLocked(session, fromWindow, false);
|
||||
if (fromWin == null || !fromWin.isFocused()) {
|
||||
return false;
|
||||
}
|
||||
final TaskFragment fromFragment = fromWin.getTaskFragment();
|
||||
if (fromFragment == null) {
|
||||
return false;
|
||||
}
|
||||
final TaskFragment adjacentFragment = fromFragment.getAdjacentTaskFragment();
|
||||
if (adjacentFragment == null || adjacentFragment.asTask() != null) {
|
||||
// Don't move the focus to another task.
|
||||
return false;
|
||||
}
|
||||
final Rect fromBounds = fromFragment.getBounds();
|
||||
final Rect adjacentBounds = adjacentFragment.getBounds();
|
||||
switch (direction) {
|
||||
case View.FOCUS_LEFT:
|
||||
if (adjacentBounds.left >= fromBounds.left) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case View.FOCUS_UP:
|
||||
if (adjacentBounds.top >= fromBounds.top) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case View.FOCUS_RIGHT:
|
||||
if (adjacentBounds.right <= fromBounds.right) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case View.FOCUS_DOWN:
|
||||
if (adjacentBounds.bottom <= fromBounds.bottom) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case View.FOCUS_BACKWARD:
|
||||
case View.FOCUS_FORWARD:
|
||||
// These are not absolute directions. Skip checking the bounds.
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
final ActivityRecord topRunningActivity = adjacentFragment.topRunningActivity(
|
||||
true /* focusableOnly */);
|
||||
if (topRunningActivity == null) {
|
||||
return false;
|
||||
}
|
||||
moveDisplayToTopInternal(topRunningActivity.getDisplayId());
|
||||
handleTaskFocusChange(topRunningActivity.getTask(), topRunningActivity);
|
||||
if (fromWin.isFocused()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Return whether layer tracing is enabled */
|
||||
public boolean isLayerTracing() {
|
||||
if (!checkCallingPermission(
|
||||
|
@ -53,6 +53,7 @@ import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.View;
|
||||
import android.window.ITaskFragmentOrganizer;
|
||||
import android.window.TaskFragmentAnimationParams;
|
||||
import android.window.TaskFragmentInfo;
|
||||
@ -695,4 +696,75 @@ public class TaskFragmentTest extends WindowTestsBase {
|
||||
mTaskFragment.getDimBounds(dimBounds);
|
||||
assertEquals(taskFragmentBounds, dimBounds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveFocusToAdjacentWindow() {
|
||||
// Setup two activities in ActivityEmbedding split.
|
||||
final Task task = createTask(mDisplayContent);
|
||||
final TaskFragment taskFragmentLeft = new TaskFragmentBuilder(mAtm)
|
||||
.setParentTask(task)
|
||||
.createActivityCount(2)
|
||||
.setOrganizer(mOrganizer)
|
||||
.setFragmentToken(new Binder())
|
||||
.build();
|
||||
final TaskFragment taskFragmentRight = new TaskFragmentBuilder(mAtm)
|
||||
.setParentTask(task)
|
||||
.createActivityCount(1)
|
||||
.setOrganizer(mOrganizer)
|
||||
.setFragmentToken(new Binder())
|
||||
.build();
|
||||
taskFragmentLeft.setAdjacentTaskFragment(taskFragmentRight);
|
||||
taskFragmentLeft.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
|
||||
taskFragmentRight.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
|
||||
task.setBounds(0, 0, 1200, 1000);
|
||||
taskFragmentLeft.setBounds(0, 0, 600, 1000);
|
||||
taskFragmentRight.setBounds(600, 0, 1200, 1000);
|
||||
final ActivityRecord appLeftTop = taskFragmentLeft.getTopMostActivity();
|
||||
final ActivityRecord appLeftBottom = taskFragmentLeft.getBottomMostActivity();
|
||||
final ActivityRecord appRightTop = taskFragmentRight.getTopMostActivity();
|
||||
appLeftTop.setVisibleRequested(true);
|
||||
appRightTop.setVisibleRequested(true);
|
||||
final WindowState winLeftTop = createAppWindow(appLeftTop, "winLeftTop");
|
||||
final WindowState winLeftBottom = createAppWindow(appLeftBottom, "winLeftBottom");
|
||||
final WindowState winRightTop = createAppWindow(appRightTop, "winRightTop");
|
||||
winLeftTop.setHasSurface(true);
|
||||
winRightTop.setHasSurface(true);
|
||||
|
||||
taskFragmentLeft.setResumedActivity(appLeftTop, "test");
|
||||
taskFragmentRight.setResumedActivity(appRightTop, "test");
|
||||
appLeftTop.setState(RESUMED, "test");
|
||||
appRightTop.setState(RESUMED, "test");
|
||||
mDisplayContent.mFocusedApp = appRightTop;
|
||||
|
||||
// Make the appLeftTop be the focused activity and ensure the focused app is updated.
|
||||
appLeftTop.moveFocusableActivityToTop("test");
|
||||
assertEquals(winLeftTop, mDisplayContent.mCurrentFocus);
|
||||
|
||||
// Send request from a non-focused window with valid direction.
|
||||
assertFalse(mWm.moveFocusToAdjacentWindow(null, winLeftBottom.mClient, View.FOCUS_RIGHT));
|
||||
// The focus should remain the same.
|
||||
assertEquals(winLeftTop, mDisplayContent.mCurrentFocus);
|
||||
|
||||
// Send request from the focused window with valid direction.
|
||||
assertTrue(mWm.moveFocusToAdjacentWindow(null, winLeftTop.mClient, View.FOCUS_RIGHT));
|
||||
// The focus should change.
|
||||
assertEquals(winRightTop, mDisplayContent.mCurrentFocus);
|
||||
|
||||
// Send request from the focused window with invalid direction.
|
||||
assertFalse(mWm.moveFocusToAdjacentWindow(null, winRightTop.mClient, View.FOCUS_UP));
|
||||
// The focus should remain the same.
|
||||
assertEquals(winRightTop, mDisplayContent.mCurrentFocus);
|
||||
|
||||
// Send request from the focused window with valid direction.
|
||||
assertTrue(mWm.moveFocusToAdjacentWindow(null, winRightTop.mClient, View.FOCUS_BACKWARD));
|
||||
// The focus should change.
|
||||
assertEquals(winLeftTop, mDisplayContent.mCurrentFocus);
|
||||
}
|
||||
|
||||
private WindowState createAppWindow(ActivityRecord app, String name) {
|
||||
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name,
|
||||
0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow());
|
||||
mWm.mWindowMap.put(win.mClient.asBinder(), win);
|
||||
return win;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user