diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java index b1b670f5e0c99..14dcdad8e2b5c 100644 --- a/core/java/android/view/RemoteAnimationTarget.java +++ b/core/java/android/view/RemoteAnimationTarget.java @@ -31,6 +31,7 @@ import static android.view.RemoteAnimationTargetProto.START_BOUNDS; import static android.view.RemoteAnimationTargetProto.START_LEASH; import static android.view.RemoteAnimationTargetProto.TASK_ID; import static android.view.RemoteAnimationTargetProto.WINDOW_CONFIGURATION; +import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import android.annotation.IntDef; import android.app.PictureInPictureParams; @@ -195,12 +196,30 @@ public class RemoteAnimationTarget implements Parcelable { */ public PictureInPictureParams pictureInPictureParams; + /** + * The {@link android.view.WindowManager.LayoutParams.WindowType} of this window. It's only used + * for non-app window. + */ + public final @WindowManager.LayoutParams.WindowType int windowType; + public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent, Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position, Rect localBounds, Rect screenSpaceBounds, WindowConfiguration windowConfig, boolean isNotInRecents, SurfaceControl startLeash, Rect startBounds, PictureInPictureParams pictureInPictureParams) { + this(taskId, mode, leash, isTranslucent, clipRect, contentInsets, prefixOrderIndex, + position, localBounds, screenSpaceBounds, windowConfig, isNotInRecents, startLeash, + startBounds, pictureInPictureParams, INVALID_WINDOW_TYPE); + } + + public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent, + Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position, + Rect localBounds, Rect screenSpaceBounds, + WindowConfiguration windowConfig, boolean isNotInRecents, + SurfaceControl startLeash, Rect startBounds, + PictureInPictureParams pictureInPictureParams, + @WindowManager.LayoutParams.WindowType int windowType) { this.mode = mode; this.taskId = taskId; this.leash = leash; @@ -217,6 +236,7 @@ public class RemoteAnimationTarget implements Parcelable { this.startLeash = startLeash; this.startBounds = startBounds == null ? null : new Rect(startBounds); this.pictureInPictureParams = pictureInPictureParams; + this.windowType = windowType; } public RemoteAnimationTarget(Parcel in) { @@ -236,6 +256,7 @@ public class RemoteAnimationTarget implements Parcelable { startLeash = in.readTypedObject(SurfaceControl.CREATOR); startBounds = in.readTypedObject(Rect.CREATOR); pictureInPictureParams = in.readTypedObject(PictureInPictureParams.CREATOR); + windowType = in.readInt(); } @Override @@ -261,6 +282,7 @@ public class RemoteAnimationTarget implements Parcelable { dest.writeTypedObject(startLeash, 0 /* flags */); dest.writeTypedObject(startBounds, 0 /* flags */); dest.writeTypedObject(pictureInPictureParams, 0 /* flags */); + dest.writeInt(windowType); } public void dump(PrintWriter pw, String prefix) { @@ -278,6 +300,7 @@ public class RemoteAnimationTarget implements Parcelable { pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration); pw.print(prefix); pw.print("leash="); pw.println(leash); pw.print(prefix); pw.print("pictureInPictureParams="); pw.println(pictureInPictureParams); + pw.print(prefix); pw.print("windowType="); pw.print(windowType); } public void dumpDebug(ProtoOutputStream proto, long fieldId) { diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 6076c2ad1a1db..5b57f1901550e 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3439,6 +3439,12 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" }, + "1931178855": { + "message": "\tnonApp=%s", + "level": "DEBUG", + "group": "WM_DEBUG_REMOTE_ANIMATIONS", + "at": "com\/android\/server\/wm\/RemoteAnimationController.java" + }, "1947239194": { "message": "Deferring rotation, still finishing previous rotation", "level": "VERBOSE", @@ -3487,6 +3493,12 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1999594750": { + "message": "startAnimation", + "level": "DEBUG", + "group": "WM_DEBUG_REMOTE_ANIMATIONS", + "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java" + }, "2018454757": { "message": "WS.removeImmediately: %s Already removed...", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 5b685b4a0499b..99289e0139b0b 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -1305,7 +1305,7 @@ public class AppTransition implements Dump { if (isTransitionSet()) { clear(); mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE; - mRemoteAnimationController = new RemoteAnimationController(mService, + mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent, remoteAnimationAdapter, mHandler); } } diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java index 5d6d51377c3f3..4ab5cd6a8d235 100644 --- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java +++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java @@ -16,6 +16,12 @@ package com.android.server.wm; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; +import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT; +import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE; + import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS; import static com.android.server.wm.AnimationAdapterProto.REMOTE; import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; @@ -26,6 +32,7 @@ import android.os.SystemClock; import android.util.proto.ProtoOutputStream; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; +import android.view.WindowManager; import com.android.internal.protolog.common.ProtoLog; import com.android.server.policy.WindowManagerPolicy; @@ -35,9 +42,11 @@ import java.util.ArrayList; class NonAppWindowAnimationAdapter implements AnimationAdapter { - private final WindowState mWindow; + private final WindowContainer mWindowContainer; private RemoteAnimationTarget mTarget; private SurfaceControl mCapturedLeash; + private SurfaceAnimator.OnAnimationFinishedCallback mCapturedLeashFinishCallback; + private @SurfaceAnimator.AnimationType int mLastAnimationType; private long mDurationHint; private long mStatusBarTransitionDelay; @@ -47,21 +56,46 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { return false; } - NonAppWindowAnimationAdapter(WindowState w, - long durationHint, long statusBarTransitionDelay) { - mWindow = w; + NonAppWindowAnimationAdapter(WindowContainer w, long durationHint, + long statusBarTransitionDelay) { + mWindowContainer = w; mDurationHint = durationHint; mStatusBarTransitionDelay = statusBarTransitionDelay; } + static RemoteAnimationTarget[] startNonAppWindowAnimations(WindowManagerService service, + DisplayContent displayContent, @WindowManager.TransitionOldType int transit, + long durationHint, long statusBarTransitionDelay, + ArrayList adaptersOut) { + final ArrayList targets = new ArrayList<>(); + if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY + || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) { + startNonAppWindowAnimationsForKeyguardExit( + service, durationHint, statusBarTransitionDelay, targets, adaptersOut); + } else if (transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT + || transit == TRANSIT_OLD_WALLPAPER_CLOSE) { + final boolean shouldAttachNavBarToApp = + displayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition() + && service.getRecentsAnimationController() == null + && displayContent.getFixedRotationAnimationController() == null; + if (shouldAttachNavBarToApp) { + startNavigationBarWindowAnimation( + displayContent, durationHint, statusBarTransitionDelay, targets, + adaptersOut); + } + } + return targets.toArray(new RemoteAnimationTarget[targets.size()]); + } + /** * Creates and starts remote animations for all the visible non app windows. * * @return RemoteAnimationTarget[] targets for all the visible non app windows */ - public static RemoteAnimationTarget[] startNonAppWindowAnimationsForKeyguardExit( - WindowManagerService service, long durationHint, long statusBarTransitionDelay) { - final ArrayList targets = new ArrayList<>(); + private static void startNonAppWindowAnimationsForKeyguardExit(WindowManagerService service, + long durationHint, long statusBarTransitionDelay, + ArrayList targets, + ArrayList adaptersOut) { final WindowManagerPolicy policy = service.mPolicy; service.mRoot.forAllWindows(nonAppWindow -> { @@ -69,12 +103,30 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { && nonAppWindow.wouldBeVisibleIfPolicyIgnored() && !nonAppWindow.isVisible()) { final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter( nonAppWindow, durationHint, statusBarTransitionDelay); + adaptersOut.add(nonAppAdapter); nonAppWindow.startAnimation(nonAppWindow.getPendingTransaction(), nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION); targets.add(nonAppAdapter.createRemoteAnimationTarget()); } }, true /* traverseTopToBottom */); - return targets.toArray(new RemoteAnimationTarget[targets.size()]); + } + + /** + * Creates and starts remote animation for the navigation bar windows. + * + * @return RemoteAnimationTarget[] targets for all the visible non app windows + */ + private static void startNavigationBarWindowAnimation(DisplayContent displayContent, + long durationHint, long statusBarTransitionDelay, + ArrayList targets, + ArrayList adaptersOut) { + final WindowState navWindow = displayContent.getDisplayPolicy().getNavigationBar(); + final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter( + navWindow.mToken, durationHint, statusBarTransitionDelay); + adaptersOut.add(nonAppAdapter); + navWindow.mToken.startAnimation(navWindow.mToken.getPendingTransaction(), + nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION); + targets.add(nonAppAdapter.createRemoteAnimationTarget()); } /** @@ -82,16 +134,39 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { */ RemoteAnimationTarget createRemoteAnimationTarget() { mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false, - new Rect(), null, mWindow.getPrefixOrderIndex(), mWindow.getLastSurfacePosition(), - mWindow.getBounds(), null, mWindow.getWindowConfiguration(), true, null, null, - null); + new Rect(), null, mWindowContainer.getPrefixOrderIndex(), + mWindowContainer.getLastSurfacePosition(), mWindowContainer.getBounds(), null, + mWindowContainer.getWindowConfiguration(), true, null, null, null, + mWindowContainer.getWindowType()); return mTarget; } @Override public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) { + ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation"); mCapturedLeash = animationLeash; + mCapturedLeashFinishCallback = finishCallback; + mLastAnimationType = type; + } + + /** + * @return the callback to call to clean up when the animation has finished. + */ + SurfaceAnimator.OnAnimationFinishedCallback getLeashFinishedCallback() { + return mCapturedLeashFinishCallback; + } + + /** + * @return the type of animation. + */ + @SurfaceAnimator.AnimationType + int getLastAnimationType() { + return mLastAnimationType; + } + + WindowContainer getWindowContainer() { + return mWindowContainer; } @Override @@ -120,8 +195,8 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { @Override public void dump(PrintWriter pw, String prefix) { pw.print(prefix); - pw.print("token="); - pw.println(mWindow.mToken); + pw.print("windowContainer="); + pw.println(mWindowContainer); if (mTarget != null) { pw.print(prefix); pw.println("Target:"); diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 6fc585e473a94..f851e3559def2 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -16,9 +16,6 @@ package com.android.server.wm; -import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; -import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; - import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS; import static com.android.server.wm.AnimationAdapterProto.REMOTE; import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; @@ -41,6 +38,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.WindowManager; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLogImpl; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.FastPrintWriter; @@ -60,10 +58,13 @@ class RemoteAnimationController implements DeathRecipient { private static final long TIMEOUT_MS = 2000; private final WindowManagerService mService; + private final DisplayContent mDisplayContent; private final RemoteAnimationAdapter mRemoteAnimationAdapter; private final ArrayList mPendingAnimations = new ArrayList<>(); private final ArrayList mPendingWallpaperAnimations = new ArrayList<>(); + @VisibleForTesting + final ArrayList mPendingNonAppAnimations = new ArrayList<>(); private final Rect mTmpRect = new Rect(); private final Handler mHandler; private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable"); @@ -72,9 +73,10 @@ class RemoteAnimationController implements DeathRecipient { private boolean mCanceled; private boolean mLinkedToDeathOfRunner; - RemoteAnimationController(WindowManagerService service, + RemoteAnimationController(WindowManagerService service, DisplayContent displayContent, RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) { mService = service; + mDisplayContent = displayContent; mRemoteAnimationAdapter = remoteAnimationAdapter; mHandler = handler; } @@ -224,12 +226,12 @@ class RemoteAnimationController implements DeathRecipient { private RemoteAnimationTarget[] createNonAppWindowAnimations( @WindowManager.TransitionOldType int transit) { ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createNonAppWindowAnimations()"); - return (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY - || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) - ? NonAppWindowAnimationAdapter.startNonAppWindowAnimationsForKeyguardExit(mService, - mRemoteAnimationAdapter.getDuration(), - mRemoteAnimationAdapter.getStatusBarTransitionDelay()) - : new RemoteAnimationTarget[0]; + return NonAppWindowAnimationAdapter.startNonAppWindowAnimations(mService, + mDisplayContent, + transit, + mRemoteAnimationAdapter.getDuration(), + mRemoteAnimationAdapter.getStatusBarTransitionDelay(), + mPendingNonAppAnimations); } private void onAnimationFinished() { @@ -267,6 +269,15 @@ class RemoteAnimationController implements DeathRecipient { mPendingWallpaperAnimations.remove(i); ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\twallpaper=%s", adapter.getToken()); } + + for (int i = mPendingNonAppAnimations.size() - 1; i >= 0; i--) { + final NonAppWindowAnimationAdapter adapter = mPendingNonAppAnimations.get(i); + adapter.getLeashFinishedCallback().onAnimationFinished( + adapter.getLastAnimationType(), adapter); + mPendingNonAppAnimations.remove(i); + ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tnonApp=%s", + adapter.getWindowContainer()); + } } catch (Exception e) { Slog.e(TAG, "Failed to finish remote animation", e); throw e; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index e3679c0d8096b..0bb9854d8020c 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -29,6 +29,7 @@ import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.os.UserHandle.USER_NULL; import static android.view.SurfaceControl.Transaction; +import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; @@ -3308,4 +3309,11 @@ class WindowContainer extends ConfigurationContainer< mListeners.remove(listener); unregisterConfigurationChangeListener(listener); } + + /** + * Returns the {@link WindowManager.LayoutParams.WindowType}. + */ + @WindowManager.LayoutParams.WindowType int getWindowType() { + return INVALID_WINDOW_TYPE; + } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 2eadcd5814175..6d88387fe25c8 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5892,4 +5892,9 @@ class WindowState extends WindowContainer implements WindowManagerP void setSurfaceTranslationY(int translationY) { mSurfaceTranslationY = translationY; } + + @Override + @WindowManager.LayoutParams.WindowType int getWindowType() { + return mAttrs.type; + } } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 8867aa747379b..5276d9c8a5f14 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -764,4 +764,9 @@ class WindowToken extends WindowContainer { forAllWindows(WindowState::clearFrozenInsetsState, true /* traverseTopToBottom */); } } + + @Override + @WindowManager.LayoutParams.WindowType int getWindowType() { + return windowType; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index c98e013479f48..956c277e18c1d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -18,11 +18,13 @@ package com.android.server.wm; 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_NAVIGATION_BAR; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -34,8 +36,11 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; +import static junit.framework.Assert.fail; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -96,7 +101,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50, true /* changeNeedsSnapshot */); mAdapter.setCallingPidUid(123, 456); runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0); - mController = new RemoteAnimationController(mWm, mAdapter, mHandler); + mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter, mHandler); } private WindowState createAppOverlayWindow() { @@ -525,6 +530,110 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { } } + @Test + public void testNonAppTarget_sendNavBar() throws Exception { + final int transit = TRANSIT_OLD_TASK_OPEN; + final AnimationAdapter adapter = setupForNonAppTargetNavBar(transit, true); + + final ArgumentCaptor nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor finishedCaptor = + ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); + verify(mMockRunner).onAnimationStart(eq(transit), any(), any(), + nonAppsCaptor.capture(), finishedCaptor.capture()); + boolean containNavTarget = false; + for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { + if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { + containNavTarget = true; + break; + } + } + assertTrue(containNavTarget); + assertEquals(1, mController.mPendingNonAppAnimations.size()); + final NonAppWindowAnimationAdapter nonAppAdapter = + mController.mPendingNonAppAnimations.get(0); + spyOn(nonAppAdapter.getLeashFinishedCallback()); + + finishedCaptor.getValue().onAnimationFinished(); + verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), + eq(adapter)); + verify(nonAppAdapter.getLeashFinishedCallback()) + .onAnimationFinished(nonAppAdapter.getLastAnimationType(), nonAppAdapter); + } + + @Test + public void testNonAppTarget_notSendNavBar_notAttachToApp() throws Exception { + final int transit = TRANSIT_OLD_TASK_OPEN; + setupForNonAppTargetNavBar(transit, false); + + final ArgumentCaptor nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + verify(mMockRunner).onAnimationStart(eq(transit), + any(), any(), nonAppsCaptor.capture(), any()); + for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { + if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { + fail("Non-app animation target must not contain navbar"); + } + } + } + + @Test + public void testNonAppTarget_notSendNavBar_controlledByFixedRotation() throws Exception { + final FixedRotationAnimationController mockController = + mock(FixedRotationAnimationController.class); + doReturn(mockController).when(mDisplayContent).getFixedRotationAnimationController(); + final int transit = TRANSIT_OLD_TASK_OPEN; + setupForNonAppTargetNavBar(transit, true); + + final ArgumentCaptor nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + verify(mMockRunner).onAnimationStart(eq(transit), + any(), any(), nonAppsCaptor.capture(), any()); + for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { + if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { + fail("Non-app animation target must not contain navbar"); + } + } + } + + @Test + public void testNonAppTarget_notSendNavBar_controlledByRecents() throws Exception { + final RecentsAnimationController mockController = + mock(RecentsAnimationController.class); + doReturn(mockController).when(mWm).getRecentsAnimationController(); + final int transit = TRANSIT_OLD_TASK_OPEN; + setupForNonAppTargetNavBar(transit, true); + + final ArgumentCaptor nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + verify(mMockRunner).onAnimationStart(eq(transit), + any(), any(), nonAppsCaptor.capture(), any()); + for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { + if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { + fail("Non-app animation target must not contain navbar"); + } + } + } + + private AnimationAdapter setupForNonAppTargetNavBar(int transit, boolean shouldAttachNavBar) { + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + mDisplayContent.mOpeningApps.add(win.mActivityRecord); + final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"); + mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); + final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); + spyOn(policy); + doReturn(shouldAttachNavBar).when(policy).shouldAttachNavBarToAppDuringTransition(); + + final AnimationAdapter adapter = mController.createRemoteAnimationRecord( + win.mActivityRecord, new Point(50, 100), null, + new Rect(50, 100, 150, 150), null).mAdapter; + adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, + mFinishedCallback); + mController.goodToGo(transit); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + return adapter; + } + private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { verify(binder, atLeast(0)).asBinder(); verifyNoMoreInteractions(binder);