Merge "Adjust cancel back navigation conditions." into udc-dev am: 67182c205b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21470967

Change-Id: I8e57e7c5c2f14e0447a7677e64ee8ddfae54f4d1
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Wei Sheng Shih
2023-02-24 06:21:32 +00:00
committed by Automerger Merge Worker
9 changed files with 217 additions and 63 deletions

View File

@@ -65,12 +65,12 @@ import android.os.Debug;
import android.os.IBinder; import android.os.IBinder;
import android.os.IProgressListener; import android.os.IProgressListener;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.RemoteCallback;
import android.os.StrictMode; import android.os.StrictMode;
import android.os.WorkSource; import android.os.WorkSource;
import android.service.voice.IVoiceInteractionSession; import android.service.voice.IVoiceInteractionSession;
import android.view.IRecentsAnimationRunner; import android.view.IRecentsAnimationRunner;
import android.view.IRemoteAnimationRunner; import android.view.IRemoteAnimationRunner;
import android.view.IWindowFocusObserver;
import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationAdapter;
import android.window.IWindowOrganizerController; import android.window.IWindowOrganizerController;
@@ -349,12 +349,13 @@ interface IActivityTaskManager {
/** /**
* Prepare the back navigation in the server. This setups the leashed for sysui to animate * Prepare the back navigation in the server. This setups the leashed for sysui to animate
* the back gesture and returns the data needed for the animation. * the back gesture and returns the data needed for the animation.
* @param focusObserver a remote callback to nofify shell when the focused window lost focus. * @param navigationObserver a remote callback to nofify shell when the focused window is gone,
or an unexpected transition has happened on the navigation target.
* @param adaptor a remote animation to be run for the back navigation plays the animation. * @param adaptor a remote animation to be run for the back navigation plays the animation.
* @return Returns the back navigation info. * @return Returns the back navigation info.
*/ */
android.window.BackNavigationInfo startBackNavigation( android.window.BackNavigationInfo startBackNavigation(
in IWindowFocusObserver focusObserver, in BackAnimationAdapter adaptor); in RemoteCallback navigationObserver, in BackAnimationAdapter adaptor);
/** /**
* registers a callback to be invoked when the screen is captured. * registers a callback to be invoked when the screen is captured.

View File

@@ -29,8 +29,9 @@ import android.content.Context;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.hardware.input.InputManager; import android.hardware.input.InputManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.RemoteCallback;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
@@ -39,7 +40,6 @@ import android.provider.Settings.Global;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.IRemoteAnimationRunner; import android.view.IRemoteAnimationRunner;
import android.view.IWindowFocusObserver;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.KeyCharacterMap; import android.view.KeyCharacterMap;
import android.view.KeyEvent; import android.view.KeyEvent;
@@ -121,23 +121,22 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private IOnBackInvokedCallback mActiveCallback; private IOnBackInvokedCallback mActiveCallback;
@VisibleForTesting @VisibleForTesting
final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() { final RemoteCallback mNavigationObserver = new RemoteCallback(
@Override new RemoteCallback.OnResultListener() {
public void focusGained(IBinder inputToken) { } @Override
@Override public void onResult(@Nullable Bundle result) {
public void focusLost(IBinder inputToken) { mShellExecutor.execute(() -> {
mShellExecutor.execute(() -> { if (!mBackGestureStarted || mPostCommitAnimationInProgress) {
if (!mBackGestureStarted || mPostCommitAnimationInProgress) { // If an uninterruptible animation is already in progress, we should
// If an uninterruptible animation is already in progress, we should ignore // ignore this due to it may cause focus lost. (alpha = 0)
// this due to it may cause focus lost. (alpha = 0) return;
return; }
ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Navigation window gone.");
setTriggerBack(false);
onGestureFinished(false);
});
} }
ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Target window lost focus.");
setTriggerBack(false);
onGestureFinished(false);
}); });
}
};
private final BackAnimationBackground mAnimationBackground; private final BackAnimationBackground mAnimationBackground;
@@ -351,7 +350,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
try { try {
mBackNavigationInfo = mActivityTaskManager.startBackNavigation( mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
mFocusObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null); mNavigationObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
onBackNavigationInfoReceived(mBackNavigationInfo); onBackNavigationInfoReceived(mBackNavigationInfo);
} catch (RemoteException remoteException) { } catch (RemoteException remoteException) {
Log.e(TAG, "Failed to initAnimation", remoteException); Log.e(TAG, "Failed to initAnimation", remoteException);

View File

@@ -25,7 +25,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
@@ -40,7 +39,6 @@ import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallback; import android.os.RemoteCallback;
import android.os.RemoteException; import android.os.RemoteException;
import android.provider.Settings; import android.provider.Settings;
@@ -341,8 +339,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
mController.setTriggerBack(true); // Fake trigger back mController.setTriggerBack(true); // Fake trigger back
// In case the focus has been changed. // In case the focus has been changed.
IBinder token = mock(IBinder.class); mController.mNavigationObserver.sendResult(null);
mController.mFocusObserver.focusLost(token);
mShellExecutor.flushAll(); mShellExecutor.flushAll();
verify(mAnimatorCallback).onBackCancelled(); verify(mAnimatorCallback).onBackCancelled();

View File

@@ -202,6 +202,7 @@ import android.os.Parcel;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.PowerManagerInternal; import android.os.PowerManagerInternal;
import android.os.Process; import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.StrictMode; import android.os.StrictMode;
@@ -228,7 +229,6 @@ import android.util.SparseArray;
import android.util.TimeUtils; import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner; import android.view.IRecentsAnimationRunner;
import android.view.IWindowFocusObserver;
import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationDefinition;
import android.view.WindowManager; import android.view.WindowManager;
@@ -1853,11 +1853,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override @Override
public BackNavigationInfo startBackNavigation( public BackNavigationInfo startBackNavigation(
IWindowFocusObserver observer, BackAnimationAdapter adapter) { RemoteCallback navigationObserver, BackAnimationAdapter adapter) {
mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"startBackNavigation()"); "startBackNavigation()");
return mBackNavigationController.startBackNavigation(observer, adapter); return mBackNavigationController.startBackNavigation(navigationObserver, adapter);
} }
/** /**

View File

@@ -253,7 +253,7 @@ public class AppTransitionController {
ArraySet<ActivityRecord> tmpOpenApps = mDisplayContent.mOpeningApps; ArraySet<ActivityRecord> tmpOpenApps = mDisplayContent.mOpeningApps;
ArraySet<ActivityRecord> tmpCloseApps = mDisplayContent.mClosingApps; ArraySet<ActivityRecord> tmpCloseApps = mDisplayContent.mClosingApps;
if (mDisplayContent.mAtmService.mBackNavigationController.isWaitBackTransition()) { if (mDisplayContent.mAtmService.mBackNavigationController.isMonitoringTransition()) {
tmpOpenApps = new ArraySet<>(mDisplayContent.mOpeningApps); tmpOpenApps = new ArraySet<>(mDisplayContent.mOpeningApps);
tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps); tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
if (mDisplayContent.mAtmService.mBackNavigationController if (mDisplayContent.mAtmService.mBackNavigationController

View File

@@ -41,7 +41,6 @@ import android.os.SystemProperties;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Slog; import android.util.Slog;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
import android.view.IWindowFocusObserver;
import android.view.RemoteAnimationTarget; import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.view.WindowInsets; import android.view.WindowInsets;
@@ -66,11 +65,11 @@ import java.util.function.Consumer;
class BackNavigationController { class BackNavigationController {
private static final String TAG = "BackNavigationController"; private static final String TAG = "BackNavigationController";
private WindowManagerService mWindowManagerService; private WindowManagerService mWindowManagerService;
private IWindowFocusObserver mFocusObserver;
private boolean mBackAnimationInProgress; private boolean mBackAnimationInProgress;
private @BackNavigationInfo.BackTargetType int mLastBackType; private @BackNavigationInfo.BackTargetType int mLastBackType;
private boolean mShowWallpaper; private boolean mShowWallpaper;
private Runnable mPendingAnimation; private Runnable mPendingAnimation;
private final NavigationMonitor mNavigationMonitor = new NavigationMonitor();
private AnimationHandler mAnimationHandler; private AnimationHandler mAnimationHandler;
private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>(); private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>();
@@ -86,6 +85,11 @@ class BackNavigationController {
return SystemProperties.getInt("persist.wm.debug.predictive_back_screenshot", 0) != 0; return SystemProperties.getInt("persist.wm.debug.predictive_back_screenshot", 0) != 0;
} }
// Notify focus window changed
void onFocusChanged(WindowState newFocus) {
mNavigationMonitor.onFocusWindowChanged(newFocus);
}
/** /**
* Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming * Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming
* back gesture animation. * back gesture animation.
@@ -96,13 +100,12 @@ class BackNavigationController {
*/ */
@VisibleForTesting @VisibleForTesting
@Nullable @Nullable
BackNavigationInfo startBackNavigation( BackNavigationInfo startBackNavigation(@NonNull RemoteCallback navigationObserver,
IWindowFocusObserver observer, BackAnimationAdapter adapter) { BackAnimationAdapter adapter) {
if (!sPredictBackEnable) { if (!sPredictBackEnable) {
return null; return null;
} }
final WindowManagerService wmService = mWindowManagerService; final WindowManagerService wmService = mWindowManagerService;
mFocusObserver = observer;
int backType = BackNavigationInfo.TYPE_UNDEFINED; int backType = BackNavigationInfo.TYPE_UNDEFINED;
@@ -202,9 +205,7 @@ class BackNavigationController {
backType = BackNavigationInfo.TYPE_CALLBACK; backType = BackNavigationInfo.TYPE_CALLBACK;
} }
infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback()); infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
if (mFocusObserver != null) { mNavigationMonitor.startMonitor(window, navigationObserver);
window.registerFocusObserver(mFocusObserver);
}
} }
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, " ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
@@ -228,10 +229,8 @@ class BackNavigationController {
|| currentActivity.isActivityTypeHome() || currentActivity.isActivityTypeHome()
|| currentActivity.mHasSceneTransition) { || currentActivity.mHasSceneTransition) {
infoBuilder.setType(BackNavigationInfo.TYPE_CALLBACK); infoBuilder.setType(BackNavigationInfo.TYPE_CALLBACK);
final WindowState finalFocusedWindow = window;
infoBuilder.setOnBackNavigationDone(new RemoteCallback(result -> infoBuilder.setOnBackNavigationDone(new RemoteCallback(result ->
onBackNavigationDone(result, finalFocusedWindow, onBackNavigationDone(result, BackNavigationInfo.TYPE_CALLBACK)));
BackNavigationInfo.TYPE_CALLBACK)));
mLastBackType = BackNavigationInfo.TYPE_CALLBACK; mLastBackType = BackNavigationInfo.TYPE_CALLBACK;
return infoBuilder.build(); return infoBuilder.build();
} }
@@ -334,16 +333,19 @@ class BackNavigationController {
WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
if (finalRemovedWindowContainer != null) { if (finalRemovedWindowContainer != null) {
final int finalBackType = backType; final int finalBackType = backType;
final WindowState finalFocusedWindow = window;
RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone( RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone(
result, finalFocusedWindow, finalBackType)); result, finalBackType));
infoBuilder.setOnBackNavigationDone(onBackNavigationDone); infoBuilder.setOnBackNavigationDone(onBackNavigationDone);
} }
mLastBackType = backType; mLastBackType = backType;
return infoBuilder.build(); return infoBuilder.build();
} }
boolean isWaitBackTransition() { boolean isMonitoringTransition() {
return isWaitBackTransition() || mNavigationMonitor.isMonitoring();
}
private boolean isWaitBackTransition() {
return mAnimationHandler.mComposed && mAnimationHandler.mWaitTransition; return mAnimationHandler.mComposed && mAnimationHandler.mWaitTransition;
} }
@@ -363,11 +365,23 @@ class BackNavigationController {
*/ */
boolean removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps, boolean removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps,
ArraySet<ActivityRecord> closeApps) { ArraySet<ActivityRecord> closeApps) {
if (!isWaitBackTransition()) { if (!isMonitoringTransition()) {
return false; return false;
} }
mTmpCloseApps.addAll(closeApps); mTmpCloseApps.addAll(closeApps);
boolean result = false; final boolean matchAnimationTargets = removeIfWaitForBackTransition(openApps, closeApps);
if (!matchAnimationTargets) {
mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps);
}
mTmpCloseApps.clear();
return matchAnimationTargets;
}
boolean removeIfWaitForBackTransition(ArraySet<ActivityRecord> openApps,
ArraySet<ActivityRecord> closeApps) {
if (!isWaitBackTransition()) {
return false;
}
// Note: TmpOpenApps is empty. Unlike shell transition, the open apps will be removed from // Note: TmpOpenApps is empty. Unlike shell transition, the open apps will be removed from
// mOpeningApps if there is no visibility change. // mOpeningApps if there is no visibility change.
if (mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) { if (mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) {
@@ -386,10 +400,76 @@ class BackNavigationController {
closeApps.removeAt(i); closeApps.removeAt(i);
} }
} }
result = true; return true;
}
return false;
}
private static class NavigationMonitor {
// The window which triggering the back navigation.
private WindowState mNavigatingWindow;
private RemoteCallback mObserver;
void startMonitor(@NonNull WindowState window, @NonNull RemoteCallback observer) {
mNavigatingWindow = window;
mObserver = observer;
}
void stopMonitor() {
mNavigatingWindow = null;
mObserver = null;
}
boolean isMonitoring() {
return mNavigatingWindow != null && mObserver != null;
}
/**
* Notify focus window changed during back navigation. This will cancel the gesture for
* scenarios like: a system window popup, or when an activity add a new window.
*
* This method should only be used to check window-level change, otherwise it may cause
* misjudgment in multi-window mode. For example: in split-screen, when user is
* navigating on the top task, bottom task can start a new task, which will gain focus for
* a short time, but we should not cancel the navigation.
*/
private void onFocusWindowChanged(WindowState newFocus) {
if (!isMonitoring() || !atSameDisplay(newFocus)) {
return;
}
// Keep navigating if either new focus == navigating window or null.
if (newFocus != null && newFocus != mNavigatingWindow
&& (newFocus.mActivityRecord == null
|| (newFocus.mActivityRecord == mNavigatingWindow.mActivityRecord))) {
EventLogTags.writeWmBackNaviCanceled("focusWindowChanged");
mObserver.sendResult(null /* result */);
}
}
/**
* Notify an unexpected transition has happened during back navigation.
*/
private void onTransitionReadyWhileNavigate(ArrayList<WindowContainer> opening,
ArrayList<WindowContainer> closing) {
if (!isMonitoring()) {
return;
}
final ArrayList<WindowContainer> all = new ArrayList<>(opening);
all.addAll(closing);
for (WindowContainer app : all) {
if (app.hasChild(mNavigatingWindow)) {
EventLogTags.writeWmBackNaviCanceled("transitionHappens");
mObserver.sendResult(null /* result */);
break;
}
}
}
private boolean atSameDisplay(WindowState newFocus) {
final int navigatingDisplayId = mNavigatingWindow.getDisplayId();
return newFocus == null || newFocus.getDisplayId() == navigatingDisplayId;
} }
mTmpCloseApps.clear();
return result;
} }
// For shell transition // For shell transition
@@ -403,8 +483,7 @@ class BackNavigationController {
* animations, and shouldn't join next transition. * animations, and shouldn't join next transition.
*/ */
boolean containsBackAnimationTargets(Transition transition) { boolean containsBackAnimationTargets(Transition transition) {
if (!mAnimationHandler.mComposed if (!isMonitoringTransition()) {
|| (transition.mType != TRANSIT_CLOSE && transition.mType != TRANSIT_TO_BACK)) {
return false; return false;
} }
final ArraySet<WindowContainer> targets = transition.mParticipants; final ArraySet<WindowContainer> targets = transition.mParticipants;
@@ -420,19 +499,19 @@ class BackNavigationController {
mTmpCloseApps.add(wc); mTmpCloseApps.add(wc);
} }
} }
final boolean result = mAnimationHandler.containsBackAnimationTargets( final boolean matchAnimationTargets = isWaitBackTransition()
mTmpOpenApps, mTmpCloseApps); && (transition.mType == TRANSIT_CLOSE || transition.mType == TRANSIT_TO_BACK)
if (result) { && mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps);
mAnimationHandler.mOpenTransitionTargetMatch = if (!matchAnimationTargets) {
mAnimationHandler.containTarget(mTmpOpenApps, true); mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps);
} }
mTmpOpenApps.clear(); mTmpOpenApps.clear();
mTmpCloseApps.clear(); mTmpCloseApps.clear();
return result; return matchAnimationTargets;
} }
boolean isMonitorTransitionTarget(WindowContainer wc) { boolean isMonitorTransitionTarget(WindowContainer wc) {
if (!mAnimationHandler.mComposed || !mAnimationHandler.mWaitTransition) { if (!isWaitBackTransition()) {
return false; return false;
} }
return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */); return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */);
@@ -951,16 +1030,13 @@ class BackNavigationController {
} }
} }
private void onBackNavigationDone(Bundle result, WindowState focusedWindow, int backType) { private void onBackNavigationDone(Bundle result, int backType) {
boolean triggerBack = result != null && result.getBoolean( boolean triggerBack = result != null && result.getBoolean(
BackNavigationInfo.KEY_TRIGGER_BACK); BackNavigationInfo.KEY_TRIGGER_BACK);
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, " ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, "
+ "triggerBack=%b", backType, triggerBack); + "triggerBack=%b", backType, triggerBack);
if (mFocusObserver != null) { mNavigationMonitor.stopMonitor();
focusedWindow.unregisterFocusObserver(mFocusObserver);
mFocusObserver = null;
}
mBackAnimationInProgress = false; mBackAnimationInProgress = false;
mShowWallpaper = false; mShowWallpaper = false;
} }

View File

@@ -3809,6 +3809,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
} }
getDisplayPolicy().focusChangedLw(oldFocus, newFocus); getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
mAtmService.mBackNavigationController.onFocusChanged(newFocus);
if (imWindowChanged && oldFocus != mInputMethodWindow) { if (imWindowChanged && oldFocus != mInputMethodWindow) {
// Focus of the input method window changed. Perform layout if needed. // Focus of the input method window changed. Perform layout if needed.

View File

@@ -66,6 +66,8 @@ option java_package com.android.server.wm
# bootanim finished: # bootanim finished:
31007 wm_boot_animation_done (time|2|3) 31007 wm_boot_animation_done (time|2|3)
# Back navigation.
31100 wm_back_navi_canceled (Reason|3)
# IME surface parent is updated. # IME surface parent is updated.
32003 imf_update_ime_parent (surface name|3) 32003 imf_update_ime_parent (surface name|3)

View File

@@ -17,8 +17,11 @@
package com.android.server.wm; package com.android.server.wm;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.window.BackNavigationInfo.typeToString; import static android.window.BackNavigationInfo.typeToString;
@@ -42,8 +45,10 @@ import android.app.ActivityOptions;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.os.Bundle; import android.os.Bundle;
import android.os.RemoteCallback;
import android.os.RemoteException; import android.os.RemoteException;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.view.WindowManager; import android.view.WindowManager;
import android.window.BackAnimationAdapter; import android.window.BackAnimationAdapter;
import android.window.BackMotionEvent; import android.window.BackMotionEvent;
@@ -320,6 +325,64 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertThat(backNavigationInfo).isNull(); assertThat(backNavigationInfo).isNull();
} }
@Test
public void testTransitionHappensCancelNavigation() {
// Create a floating task and a fullscreen task, then navigating on fullscreen task.
// The navigation should not been cancelled when transition happens on floating task, and
// only be cancelled when transition happens on the navigating task.
final Task floatingTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
ACTIVITY_TYPE_STANDARD);
final ActivityRecord baseFloatingActivity = createActivityRecord(floatingTask);
final Task fullscreenTask = createTopTaskWithActivity();
withSystemCallback(fullscreenTask);
final ActivityRecord baseFullscreenActivity = fullscreenTask.getTopMostActivity();
final CountDownLatch navigationObserver = new CountDownLatch(1);
startBackNavigation(navigationObserver);
final ArraySet<ActivityRecord> opening = new ArraySet<>();
final ArraySet<ActivityRecord> closing = new ArraySet<>();
final ActivityRecord secondFloatingActivity = createActivityRecord(floatingTask);
opening.add(secondFloatingActivity);
closing.add(baseFloatingActivity);
mBackNavigationController.removeIfContainsBackAnimationTargets(opening, closing);
assertEquals("Transition happen on an irrelevant task, callback should not been called",
1, navigationObserver.getCount());
// Create a new activity above navigation target, the transition should cancel navigation.
final ActivityRecord topFullscreenActivity = createActivityRecord(fullscreenTask);
opening.clear();
closing.clear();
opening.add(topFullscreenActivity);
closing.add(baseFullscreenActivity);
mBackNavigationController.removeIfContainsBackAnimationTargets(opening, closing);
assertEquals("Transition happen on navigation task, callback should have been called",
0, navigationObserver.getCount());
}
@Test
public void testWindowFocusChangeCancelNavigation() {
Task task = createTopTaskWithActivity();
withSystemCallback(task);
WindowState focusWindow = task.getTopVisibleAppMainWindow();
final CountDownLatch navigationObserver = new CountDownLatch(1);
startBackNavigation(navigationObserver);
mBackNavigationController.onFocusChanged(null);
assertEquals("change focus to null, callback should not have been called",
1, navigationObserver.getCount());
mBackNavigationController.onFocusChanged(focusWindow);
assertEquals("change focus back, callback should not have been called",
1, navigationObserver.getCount());
WindowState newWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlayWindow");
addToWindowMap(newWindow, true);
mBackNavigationController.onFocusChanged(newWindow);
assertEquals("Focus change, callback should have been called",
0, navigationObserver.getCount());
}
private IOnBackInvokedCallback withSystemCallback(Task task) { private IOnBackInvokedCallback withSystemCallback(Task task) {
IOnBackInvokedCallback callback = createOnBackInvokedCallback(); IOnBackInvokedCallback callback = createOnBackInvokedCallback();
task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo( task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo(
@@ -336,7 +399,14 @@ public class BackNavigationControllerTests extends WindowTestsBase {
@Nullable @Nullable
private BackNavigationInfo startBackNavigation() { private BackNavigationInfo startBackNavigation() {
return mBackNavigationController.startBackNavigation(null, mBackAnimationAdapter); return mBackNavigationController.startBackNavigation(
createNavigationObserver(null), mBackAnimationAdapter);
}
@Nullable
private BackNavigationInfo startBackNavigation(CountDownLatch navigationObserverLatch) {
return mBackNavigationController.startBackNavigation(
createNavigationObserver(navigationObserverLatch), mBackAnimationAdapter);
} }
@NonNull @NonNull
@@ -371,6 +441,14 @@ public class BackNavigationControllerTests extends WindowTestsBase {
}; };
} }
private RemoteCallback createNavigationObserver(CountDownLatch latch) {
return new RemoteCallback(result -> {
if (latch != null) {
latch.countDown();
}
});
}
private Task initHomeActivity() { private Task initHomeActivity() {
final Task task = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask(); final Task task = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
task.forAllLeafTasks((t) -> { task.forAllLeafTasks((t) -> {