Merge "Do not change focus when back gesture starts" into main
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index fedbe4a..42d66ce 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -366,13 +366,8 @@
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 3a1d833..2f204f9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -727,8 +727,6 @@
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.
@@ -7269,7 +7267,7 @@
// 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
@@ -11218,11 +11216,6 @@
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.
*
@@ -12554,15 +12547,8 @@
* @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 8421765..0f8d68b 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 @@
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 9371149..25c8208 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 @@
}
/**
+ * 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 30ea086..f437a43 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.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.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;
@@ -9377,40 +9377,77 @@
}
/**
- * 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;
}
- moveFocusToActivity(adjacentTopActivity);
+ return adjacentTopActivity;
+ }
+
+ @NonNull
+ WindowState getMostRecentUsedEmbeddedWindowForBack(@NonNull WindowState focusedWindow) {
+ final ActivityRecord focusedActivity = focusedWindow.getActivityRecord();
+ if (focusedActivity == null) {
+ // Not an Activity.
+ return focusedWindow;
+ }
+
+ 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 63c14b9..afa22bc 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.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 @@
}
@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);
}
/**