diff options
6 files changed, 96 insertions, 58 deletions
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index fedbe4a65e07..42d66ce6bf1b 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -366,13 +366,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall Log.e(TAG, "Received invalid input event"); return; } - try { - vri.processingBackKey(true); - vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */, - true /* processImmediately */); - } finally { - vri.processingBackKey(false); - } + vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */, + true /* processImmediately */); }); } }; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index e5b17c8d1001..50e3b8afb92d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -727,8 +727,6 @@ public final class ViewRootImpl implements ViewParent, boolean mUpcomingWindowFocus; @GuardedBy("this") boolean mUpcomingInTouchMode; - // While set, allow this VRI to handle back key without drop it. - private boolean mProcessingBackKey; /** * Compatibility {@link OnBackInvokedCallback} for windowless window, to forward the back * key event host app. @@ -7265,7 +7263,7 @@ public final class ViewRootImpl implements ViewParent, // Find a reason for dropping or canceling the event. final String reason; // The embedded window is focused, allow this VRI to handle back key. - if (!mAttachInfo.mHasWindowFocus && !(mProcessingBackKey && isBack(q.mEvent)) + if (!mAttachInfo.mHasWindowFocus && !isBack(q.mEvent) && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) && !isAutofillUiShowing()) { // This is a non-pointer event and the window doesn't currently have input focus @@ -11213,11 +11211,6 @@ public final class ViewRootImpl implements ViewParent, mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, listener).sendToTarget(); } - // Make this VRI able to process back key without drop it. - void processingBackKey(boolean processing) { - mProcessingBackKey = processing; - } - /** * Collect and include any ScrollCaptureCallback instances registered with the window. * @@ -12549,15 +12542,8 @@ public final class ViewRootImpl implements ViewParent, * @return whether the event was handled (i.e. onKeyPreIme consumed it if preImeOnly=true) */ public boolean injectBackKeyEvents(boolean preImeOnly) { - boolean consumed; - try { - processingBackKey(true); - sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly); - consumed = sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly); - } finally { - processingBackKey(false); - } - return consumed; + sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly); + return sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly); } private boolean sendBackKeyEvent(int action, boolean preImeOnly) { diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 8421765060ce..0f8d68b713a7 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -166,15 +166,14 @@ class BackNavigationController { return null; } - // Move focus to the top embedded window if possible - if (mWindowManagerService.moveFocusToAdjacentEmbeddedWindow(window)) { - window = wmService.getFocusedWindowLocked(); - if (window == null) { - Slog.e(TAG, "New focused window is null, returning null."); - return null; - } + // Updating the window to the most recently used one among the embedded windows + // that are displayed adjacently, unless the IME is visible. + // When the IME is visible, the IME is displayed on top of embedded activities. + // In that case, the back event should still be delivered to focused activity in + // order to dismiss the IME. + if (!window.getDisplayContent().getImeContainer().isVisible()) { + window = mWindowManagerService.getMostRecentUsedEmbeddedWindowForBack(window); } - if (!window.isDrawn()) { ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Focused window didn't have a valid surface drawn."); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 93711497f590..25c820818bbd 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3895,6 +3895,22 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } /** + * Returns the focused window of the given Activity if the Activity is focused. + */ + WindowState findFocusedWindow(ActivityRecord activityRecord) { + final ActivityRecord tmpApp = mFocusedApp; + mTmpWindow = null; + try { + mFocusedApp = activityRecord; + // mFindFocusedWindow will populate mTmpWindow with the new focused window when found. + activityRecord.forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */); + } finally { + mFocusedApp = tmpApp; + } + return mTmpWindow; + } + + /** * Update the focused window and make some adjustments if the focus has changed. * * @param mode Indicates the situation we are in. Possible modes are: diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ebbf6e346e91..782d37a44b56 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -54,7 +54,6 @@ import static android.service.dreams.Flags.dreamHandlesConfirmKeys; import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; -import static android.view.flags.Flags.sensitiveContentAppProtection; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; @@ -97,6 +96,7 @@ import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER; import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW; import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN; +import static android.view.flags.Flags.sensitiveContentAppProtection; import static android.window.WindowProviderService.isWindowProviderService; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; @@ -9379,40 +9379,77 @@ public class WindowManagerService extends IWindowManager.Stub } /** - * Move focus to the adjacent embedded activity if the adjacent activity is more recently - * created or has a window more recently added. + * Returns the Activity that has the most recently created window in the adjacent activities + * if any. */ - boolean moveFocusToAdjacentEmbeddedWindow(@NonNull WindowState focusedWindow) { - final TaskFragment taskFragment = focusedWindow.getTaskFragment(); + @NonNull + ActivityRecord getMostRecentActivityInAdjacent(@NonNull ActivityRecord focusedActivity) { + final TaskFragment taskFragment = focusedActivity.getTaskFragment(); if (taskFragment == null) { - // Skip if not an Activity window. - return false; + // Return if activity no attached. + return focusedActivity; } if (!Flags.embeddedActivityBackNavFlag()) { - // Skip if flag is not enabled. - return false; + // Return if flag is not enabled. + return focusedActivity; } - if (!focusedWindow.mActivityRecord.isEmbedded()) { - // Skip if the focused activity is not embedded - return false; + if (!focusedActivity.isEmbedded()) { + // Return if the focused activity is not embedded. + return focusedActivity; } final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment(); final ActivityRecord adjacentTopActivity = adjacentTaskFragment != null ? adjacentTaskFragment.topRunningActivity() : null; if (adjacentTopActivity == null) { - return false; + // Return if no adjacent activity. + return focusedActivity; } if (adjacentTopActivity.getLastWindowCreateTime() - < focusedWindow.mActivityRecord.getLastWindowCreateTime()) { - // Skip if the current focus activity has more recently active window. - return false; + < focusedActivity.getLastWindowCreateTime()) { + // Return if the current focus activity has more recently active window. + return focusedActivity; + } + + return adjacentTopActivity; + } + + @NonNull + WindowState getMostRecentUsedEmbeddedWindowForBack(@NonNull WindowState focusedWindow) { + final ActivityRecord focusedActivity = focusedWindow.getActivityRecord(); + if (focusedActivity == null) { + // Not an Activity. + return focusedWindow; } - moveFocusToActivity(adjacentTopActivity); + final ActivityRecord mostRecentActivityInAdjacent = getMostRecentActivityInAdjacent( + focusedActivity); + if (mostRecentActivityInAdjacent == focusedActivity) { + // Already be the most recent window. + return focusedWindow; + } + + // Looks for a candidate focused window on the adjacent Activity for the back event. + final WindowState candidate = + mostRecentActivityInAdjacent.getDisplayContent().findFocusedWindow( + mostRecentActivityInAdjacent); + return candidate != null ? candidate : focusedWindow; + } + + /** + * Move focus to the adjacent embedded activity if the adjacent activity is more recently + * created or has a window more recently added. + * <p> + * Returns {@code true} if the focused window is changed. Otherwise, returns {@code false}. + */ + boolean moveFocusToAdjacentEmbeddedWindow(@NonNull WindowState focusedWindow) { + final ActivityRecord mostRecentActivityInAdjacent = getMostRecentActivityInAdjacent( + focusedWindow.getActivityRecord()); + + moveFocusToActivity(mostRecentActivityInAdjacent); return !focusedWindow.isFocused(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index 63c14b90958f..afa22bc5eae8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -67,6 +67,7 @@ import android.window.IOnBackInvokedCallback; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedCallbackInfo; import android.window.OnBackInvokedDispatcher; +import android.window.TaskFragmentOrganizer; import android.window.TaskSnapshot; import android.window.WindowOnBackInvokedDispatcher; @@ -670,25 +671,29 @@ public class BackNavigationControllerTests extends WindowTestsBase { } @Test - public void testAdjacentFocusInActivityEmbedding() { + public void testBackOnMostRecentWindowInActivityEmbedding() { mSetFlagsRule.enableFlags(Flags.FLAG_EMBEDDED_ACTIVITY_BACK_NAV_FLAG); final Task task = createTask(mDefaultDisplay); - final TaskFragment primaryTf = createTaskFragmentWithActivity(task); - final TaskFragment secondaryTf = createTaskFragmentWithActivity(task); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TaskFragment primaryTf = createTaskFragmentWithEmbeddedActivity(task, organizer); + final TaskFragment secondaryTf = createTaskFragmentWithEmbeddedActivity(task, organizer); final ActivityRecord primaryActivity = primaryTf.getTopMostActivity(); final ActivityRecord secondaryActivity = secondaryTf.getTopMostActivity(); primaryTf.setAdjacentTaskFragment(secondaryTf); secondaryTf.setAdjacentTaskFragment(primaryTf); - final WindowState windowState = mock(WindowState.class); - windowState.mActivityRecord = primaryActivity; - doReturn(windowState).when(mWm).getFocusedWindowLocked(); - doReturn(primaryTf).when(windowState).getTaskFragment(); + final WindowState primaryWindow = mock(WindowState.class); + final WindowState secondaryWindow = mock(WindowState.class); + doReturn(primaryActivity).when(primaryWindow).getActivityRecord(); + doReturn(secondaryActivity).when(secondaryWindow).getActivityRecord(); doReturn(1L).when(primaryActivity).getLastWindowCreateTime(); doReturn(2L).when(secondaryActivity).getLastWindowCreateTime(); + doReturn(mDisplayContent).when(primaryActivity).getDisplayContent(); + doReturn(secondaryWindow).when(mDisplayContent).findFocusedWindow(eq(secondaryActivity)); - startBackNavigation(); - verify(mWm).moveFocusToActivity(eq(secondaryActivity)); + final WindowState mostRecentUsedWindow = + mWm.getMostRecentUsedEmbeddedWindowForBack(primaryWindow); + assertThat(mostRecentUsedWindow).isEqualTo(secondaryWindow); } /** |