Loading core/java/android/view/InsetsController.java +0 −1 Original line number Diff line number Diff line Loading @@ -841,7 +841,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return mLastDispatchedState; } @VisibleForTesting public boolean onStateChanged(InsetsState state) { boolean stateChanged = false; if (!CAPTION_ON_SHELL) { Loading services/core/java/com/android/server/wm/InsetsPolicy.java +149 −192 Original line number Diff line number Diff line Loading @@ -21,12 +21,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.InsetsController.ANIMATION_TYPE_HIDE; import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN; import static android.view.InsetsSource.ID_IME; import static android.view.SyncRtSurfaceTransactionApplier.applyParams; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; Loading @@ -39,16 +34,13 @@ import android.app.StatusBarManager; import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.res.Resources; import android.os.Handler; import android.os.IBinder; import android.util.SparseArray; import android.view.InsetsAnimationControlCallbacks; import android.view.InsetsAnimationControlImpl; import android.view.InsetsAnimationControlRunner; import android.view.InsetsController; import android.view.InsetsFrameProvider; import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.InternalInsetsAnimationController; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; import android.view.WindowInsets; Loading @@ -56,15 +48,15 @@ import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowInsetsAnimationControlListener; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.server.DisplayThread; import com.android.server.statusbar.StatusBarManagerInternal; import java.io.PrintWriter; import java.util.List; /** * Policy that implements who gets control over the windows generating insets. Loading @@ -79,48 +71,19 @@ class InsetsPolicy { private final DisplayContent mDisplayContent; private final DisplayPolicy mPolicy; /** For resetting visibilities of insets sources. */ private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() { /** Used to show system bars transiently. This won't affect the layout. */ private final InsetsControlTarget mTransientControlTarget; @Override public void notifyInsetsControlChanged() { boolean hasLeash = false; final InsetsSourceControl[] controls = mStateController.getControlsForDispatch(this); if (controls == null) { return; } for (InsetsSourceControl control : controls) { if (isTransient(control.getType())) { // The visibilities of transient bars will be handled with animations. continue; } final SurfaceControl leash = control.getLeash(); if (leash != null) { hasLeash = true; // We use alpha to control the visibility here which aligns the logic at // SurfaceAnimator.createAnimationLeash final boolean visible = (control.getType() & WindowInsets.Type.defaultVisible()) != 0; mDisplayContent.getPendingTransaction().setAlpha(leash, visible ? 1f : 0f); } } if (hasLeash) { mDisplayContent.scheduleAnimation(); } } }; /** Used to show system bars permanently. This will affect the layout. */ private final InsetsControlTarget mPermanentControlTarget; private WindowState mFocusedWin; private final BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR); private final BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR); private @InsetsType int mShowingTransientTypes; private @InsetsType int mForcedShowingTypes; private boolean mAnimatingShown; private final boolean mHideNavBarForKeyboard; private final float[] mTmpFloat9 = new float[9]; InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) { mStateController = stateController; Loading @@ -128,6 +91,10 @@ class InsetsPolicy { mPolicy = displayContent.getDisplayPolicy(); final Resources r = mPolicy.getContext().getResources(); mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard); mTransientControlTarget = new ControlTarget( stateController, displayContent.mWmService.mH, "TransientControlTarget"); mPermanentControlTarget = new ControlTarget( stateController, displayContent.mWmService.mH, "PermanentControlTarget"); } /** Updates the target which can control system bars. */ Loading @@ -144,13 +111,13 @@ class InsetsPolicy { final WindowState topApp = mPolicy.getTopFullscreenOpaqueWindow(); mStateController.onBarControlTargetChanged( statusControlTarget, statusControlTarget == mDummyControlTarget statusControlTarget == mTransientControlTarget ? getStatusControlTarget(focusedWin, true /* fake */) : statusControlTarget == notificationShade ? getStatusControlTarget(topApp, true /* fake */) : null, navControlTarget, navControlTarget == mDummyControlTarget navControlTarget == mTransientControlTarget ? getNavControlTarget(focusedWin, true /* fake */) : navControlTarget == notificationShade ? getNavControlTarget(topApp, true /* fake */) Loading Loading @@ -200,17 +167,17 @@ class InsetsPolicy { mFocusedWin, (showingTransientTypes & (Type.statusBars() | Type.navigationBars())) != 0, isGestureOnSystemBar); // The leashes can be created while updating bar control target. The surface transaction // of the new leashes might not be applied yet. The callback posted here ensures we can // get the valid leashes because the surface transaction will be applied in the next // animation frame which will be triggered if a new leash is created. mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> { synchronized (mDisplayContent.mWmService.mGlobalLock) { startAnimation(true /* show */, null /* callback */); } }); } @VisibleForTesting InsetsControlTarget getTransientControlTarget() { return mTransientControlTarget; } @VisibleForTesting InsetsControlTarget getPermanentControlTarget() { return mPermanentControlTarget; } void hideTransient() { Loading @@ -223,24 +190,9 @@ class InsetsPolicy { /* areVisible= */ false, /* wereRevealedFromSwipeOnSystemBar= */ false); startAnimation(false /* show */, () -> { synchronized (mDisplayContent.mWmService.mGlobalLock) { final SparseArray<InsetsSourceProvider> providers = mStateController.getSourceProviders(); for (int i = providers.size() - 1; i >= 0; i--) { final InsetsSourceProvider provider = providers.valueAt(i); if (!isTransient(provider.getSource().getType())) { continue; } // We are about to clear mShowingTransientTypes, we don't want the transient bar // can cause insets on the client. Restore the client visibility. provider.setClientVisible(false); } mShowingTransientTypes = 0; updateBarControlTarget(mFocusedWin); } }); } boolean isTransient(@InsetsType int type) { return (mShowingTransientTypes & type) != 0; Loading Loading @@ -502,7 +454,7 @@ class InsetsPolicy { private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin, boolean fake) { if (!fake && isTransient(Type.statusBars())) { return mDummyControlTarget; return mTransientControlTarget; } final WindowState notificationShade = mPolicy.getNotificationShade(); if (focusedWin == notificationShade) { Loading @@ -519,13 +471,13 @@ class InsetsPolicy { if (areTypesForciblyShowing(Type.statusBars())) { // Status bar is forcibly shown. We don't want the client to control the status bar, and // we will dispatch the real visibility of status bar to the client. return null; return mPermanentControlTarget; } if (forceShowsStatusBarTransiently() && !fake) { // Status bar is forcibly shown transiently, and its new visibility won't be // dispatched to the client so that we can keep the layout stable. We will dispatch the // fake control to the client, so that it can re-show the bar during this scenario. return mDummyControlTarget; return mTransientControlTarget; } if (!canBeTopFullscreenOpaqueWindow(focusedWin) && mPolicy.topAppHidesSystemBar(Type.statusBars()) Loading Loading @@ -556,7 +508,7 @@ class InsetsPolicy { return null; } if (!fake && isTransient(Type.navigationBars())) { return mDummyControlTarget; return mTransientControlTarget; } if (focusedWin == mPolicy.getNotificationShade()) { // Notification shade has control anyways, no reason to force anything. Loading @@ -579,13 +531,13 @@ class InsetsPolicy { if (areTypesForciblyShowing(Type.navigationBars())) { // Navigation bar is forcibly shown. We don't want the client to control the navigation // bar, and we will dispatch the real visibility of navigation bar to the client. return null; return mPermanentControlTarget; } if (forceShowsNavigationBarTransiently() && !fake) { // Navigation bar is forcibly shown transiently, and its new visibility won't be // dispatched to the client so that we can keep the layout stable. We will dispatch the // fake control to the client, so that it can re-show the bar during this scenario. return mDummyControlTarget; return mTransientControlTarget; } final WindowState notificationShade = mPolicy.getNotificationShade(); if (!canBeTopFullscreenOpaqueWindow(focusedWin) Loading Loading @@ -662,34 +614,6 @@ class InsetsPolicy { && (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; } @VisibleForTesting void startAnimation(boolean show, Runnable callback) { @InsetsType int typesReady = 0; final SparseArray<InsetsSourceControl> controlsReady = new SparseArray<>(); final InsetsSourceControl[] controls = mStateController.getControlsForDispatch(mDummyControlTarget); if (controls == null) { if (callback != null) { DisplayThread.getHandler().post(callback); } return; } for (InsetsSourceControl control : controls) { if (isTransient(control.getType()) && control.getLeash() != null) { typesReady |= control.getType(); controlsReady.put(control.getId(), new InsetsSourceControl(control)); } } controlAnimationUnchecked(typesReady, controlsReady, show, callback); } private void controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) { InsetsPolicyAnimationControlListener listener = new InsetsPolicyAnimationControlListener(show, callback, typesReady); listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show); } private void dispatchTransientSystemBarsVisibilityChanged( @Nullable WindowState focusedWindow, boolean areVisible, Loading Loading @@ -760,105 +684,138 @@ class InsetsPolicy { } } private class InsetsPolicyAnimationControlListener extends InsetsController.InternalAnimationControlListener { Runnable mFinishCallback; InsetsPolicyAnimationControlCallbacks mControlCallbacks; private static class ControlTarget implements InsetsControlTarget { InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) { super(show, false /* hasCallbacks */, types, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, false /* disable */, 0 /* floatingImeBottomInsets */, null /* loggingListener */, null /* jankContext */); mFinishCallback = finishCallback; mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this); private final InsetsState mState = new InsetsState(); private final InsetsController mInsetsController; private final InsetsStateController mStateController; private final String mName; ControlTarget(InsetsStateController stateController, Handler handler, String name) { mStateController = stateController; mInsetsController = new InsetsController(new Host(handler, name)); mName = name; } @Override protected void onAnimationFinish() { super.onAnimationFinish(); if (mFinishCallback != null) { DisplayThread.getHandler().post(mFinishCallback); public void notifyInsetsControlChanged() { mState.set(mStateController.getRawInsetsState(), true /* copySources */); mInsetsController.onStateChanged(mState); mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this)); } @Override public String toString() { return mName; } } private class InsetsPolicyAnimationControlCallbacks implements InsetsAnimationControlCallbacks { private InsetsAnimationControlImpl mAnimationControl = null; private InsetsPolicyAnimationControlListener mListener; private static class Host implements InsetsController.Host { InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) { mListener = listener; private final float[] mTmpFloat9 = new float[9]; private final Handler mHandler; private final String mName; Host(Handler handler, String name) { mHandler = handler; mName = name; } private void controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show) { if (typesReady == 0) { // nothing to animate. return; @Override public Handler getHandler() { return mHandler; } mAnimatingShown = show; final InsetsState state = mFocusedWin.getInsetsState(); @Override public void notifyInsetsChanged() { } // We are about to playing the default animation. Passing a null frame indicates // the controlled types should be animated regardless of the frame. mAnimationControl = new InsetsAnimationControlImpl(controls, null /* frame */, state, mListener, typesReady, this, mListener.getDurationMs(), getInsetsInterpolator(), show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, null /* translator */, null /* statsToken */); SurfaceAnimationThread.getHandler().post( () -> mListener.onReady(mAnimationControl, typesReady)); } @Override public void dispatchWindowInsetsAnimationPrepare( @NonNull WindowInsetsAnimation animation) { } /** Called on SurfaceAnimationThread without global WM lock held. */ @Override public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { if (mAnimationControl.applyChangeInsets(null /* outState */)) { mAnimationControl.finish(mAnimatingShown); } public Bounds dispatchWindowInsetsAnimationStart( @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds) { return bounds; } @Override public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) { // Nothing's needed here. Finish steps is handled in the listener // onAnimationFinished callback. public WindowInsets dispatchWindowInsetsAnimationProgress( @NonNull WindowInsets insets, @NonNull List<WindowInsetsAnimation> runningAnimations) { return insets; } /** Called on SurfaceAnimationThread without global WM lock held. */ @Override public void applySurfaceParams( final SyncRtSurfaceTransactionApplier.SurfaceParams... params) { SurfaceControl.Transaction t = new SurfaceControl.Transaction(); for (int i = params.length - 1; i >= 0; i--) { SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i]; applyParams(t, surfaceParams, mTmpFloat9); public void dispatchWindowInsetsAnimationEnd( @NonNull WindowInsetsAnimation animation) { } @Override public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... p) { final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); for (int i = p.length - 1; i >= 0; i--) { SyncRtSurfaceTransactionApplier.applyParams(t, p[i], mTmpFloat9); } t.apply(); t.close(); } // Since we don't push applySurfaceParams to a Handler-queue we don't need // to push release in this case. @Override public void releaseSurfaceControlFromRt(SurfaceControl sc) { sc.release(); public void updateRequestedVisibleTypes(int types) { } @Override public boolean hasAnimationCallbacks() { return false; } @Override public <T extends InsetsAnimationControlRunner & InternalInsetsAnimationController> void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds) { public void setSystemBarsAppearance(int appearance, int mask) { } @Override public int getSystemBarsAppearance() { return 0; } @Override public void reportPerceptible(int types, boolean perceptible) { // No-op for now - only client windows report perceptibility for now, with policy // controllers assumed to always be perceptible. public void setSystemBarsBehavior(int behavior) { } @Override public int getSystemBarsBehavior() { return BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; } @Override public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) { surfaceControl.release(); } @Override public void addOnPreDrawRunnable(Runnable r) { } @Override public void postInsetsAnimationCallback(Runnable r) { } @Override public InputMethodManager getInputMethodManager() { return null; } @Nullable @Override public String getRootViewTitle() { return mName; } @Override public int dipToPx(int dips) { return 0; } @Nullable @Override public IBinder getWindowToken() { return null; } } } services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +0 −13 Original line number Diff line number Diff line Loading @@ -31,17 +31,12 @@ import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; import android.app.StatusBarManager; Loading Loading @@ -268,8 +263,6 @@ public class InsetsPolicyTest extends WindowTestsBase { navBar.setHasSurface(true); navBarProvider.setServerVisible(true); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); spyOn(policy); doNothing().when(policy).startAnimation(anyBoolean(), any()); // Make both system bars invisible. mAppWindow.setRequestedVisibleTypes( Loading Loading @@ -305,8 +298,6 @@ public class InsetsPolicyTest extends WindowTestsBase { addNavigationBar().getControllableInsetProvider().setServerVisible(true); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); spyOn(policy); doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(mAppWindow); policy.showTransient(navigationBars() | statusBars(), true /* isGestureOnSystemBar */); Loading Loading @@ -341,8 +332,6 @@ public class InsetsPolicyTest extends WindowTestsBase { mAppWindow.mAboveInsetsState.addSource(navBarSource); mAppWindow.mAboveInsetsState.addSource(statusBarSource); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); spyOn(policy); doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(mAppWindow); policy.showTransient(navigationBars() | statusBars(), true /* isGestureOnSystemBar */); Loading Loading @@ -390,8 +379,6 @@ public class InsetsPolicyTest extends WindowTestsBase { final WindowState app2 = addWindow(TYPE_APPLICATION, "app"); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); spyOn(policy); doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(app); policy.showTransient(navigationBars() | statusBars(), true /* isGestureOnSystemBar */); Loading services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +4 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,10 @@ class TestDisplayContent extends DisplayContent { spyOn(inputMonitor); doNothing().when(inputMonitor).resumeDispatchingLw(any()); final InsetsPolicy insetsPolicy = getInsetsPolicy(); WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget()); WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getTransientControlTarget()); // For devices that set the sysprop ro.bootanim.set_orientation_<display_id> // See DisplayRotation#readDefaultDisplayRotation for context. // Without that, meaning of height and width in context of the tests can be swapped if Loading services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +13 −0 Original line number Diff line number Diff line Loading @@ -222,6 +222,10 @@ class WindowTestsBase extends SystemServiceTestsBase { displayPolicy.finishWindowsDrawn(); displayPolicy.finishScreenTurningOn(); final InsetsPolicy insetsPolicy = mDefaultDisplay.getInsetsPolicy(); suppressInsetsAnimation(insetsPolicy.getTransientControlTarget()); suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget()); mTransaction = mSystemServicesTestRule.mTransaction; mMockSession = mock(Session.class); Loading Loading @@ -278,6 +282,15 @@ class WindowTestsBase extends SystemServiceTestsBase { checkDeviceSpecificOverridesNotApplied(); } /** * The test doesn't create real SurfaceControls, but mocked ones. This prevents the target from * controlling them, or it will cause {@link NullPointerException}. */ static void suppressInsetsAnimation(InsetsControlTarget target) { spyOn(target); Mockito.doNothing().when(target).notifyInsetsControlChanged(); } @After public void tearDown() throws Exception { if (mUseFakeSettingsProvider) { Loading Loading
core/java/android/view/InsetsController.java +0 −1 Original line number Diff line number Diff line Loading @@ -841,7 +841,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return mLastDispatchedState; } @VisibleForTesting public boolean onStateChanged(InsetsState state) { boolean stateChanged = false; if (!CAPTION_ON_SHELL) { Loading
services/core/java/com/android/server/wm/InsetsPolicy.java +149 −192 Original line number Diff line number Diff line Loading @@ -21,12 +21,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.InsetsController.ANIMATION_TYPE_HIDE; import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN; import static android.view.InsetsSource.ID_IME; import static android.view.SyncRtSurfaceTransactionApplier.applyParams; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; Loading @@ -39,16 +34,13 @@ import android.app.StatusBarManager; import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.res.Resources; import android.os.Handler; import android.os.IBinder; import android.util.SparseArray; import android.view.InsetsAnimationControlCallbacks; import android.view.InsetsAnimationControlImpl; import android.view.InsetsAnimationControlRunner; import android.view.InsetsController; import android.view.InsetsFrameProvider; import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.InternalInsetsAnimationController; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; import android.view.WindowInsets; Loading @@ -56,15 +48,15 @@ import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowInsetsAnimationControlListener; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.server.DisplayThread; import com.android.server.statusbar.StatusBarManagerInternal; import java.io.PrintWriter; import java.util.List; /** * Policy that implements who gets control over the windows generating insets. Loading @@ -79,48 +71,19 @@ class InsetsPolicy { private final DisplayContent mDisplayContent; private final DisplayPolicy mPolicy; /** For resetting visibilities of insets sources. */ private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() { /** Used to show system bars transiently. This won't affect the layout. */ private final InsetsControlTarget mTransientControlTarget; @Override public void notifyInsetsControlChanged() { boolean hasLeash = false; final InsetsSourceControl[] controls = mStateController.getControlsForDispatch(this); if (controls == null) { return; } for (InsetsSourceControl control : controls) { if (isTransient(control.getType())) { // The visibilities of transient bars will be handled with animations. continue; } final SurfaceControl leash = control.getLeash(); if (leash != null) { hasLeash = true; // We use alpha to control the visibility here which aligns the logic at // SurfaceAnimator.createAnimationLeash final boolean visible = (control.getType() & WindowInsets.Type.defaultVisible()) != 0; mDisplayContent.getPendingTransaction().setAlpha(leash, visible ? 1f : 0f); } } if (hasLeash) { mDisplayContent.scheduleAnimation(); } } }; /** Used to show system bars permanently. This will affect the layout. */ private final InsetsControlTarget mPermanentControlTarget; private WindowState mFocusedWin; private final BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR); private final BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR); private @InsetsType int mShowingTransientTypes; private @InsetsType int mForcedShowingTypes; private boolean mAnimatingShown; private final boolean mHideNavBarForKeyboard; private final float[] mTmpFloat9 = new float[9]; InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) { mStateController = stateController; Loading @@ -128,6 +91,10 @@ class InsetsPolicy { mPolicy = displayContent.getDisplayPolicy(); final Resources r = mPolicy.getContext().getResources(); mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard); mTransientControlTarget = new ControlTarget( stateController, displayContent.mWmService.mH, "TransientControlTarget"); mPermanentControlTarget = new ControlTarget( stateController, displayContent.mWmService.mH, "PermanentControlTarget"); } /** Updates the target which can control system bars. */ Loading @@ -144,13 +111,13 @@ class InsetsPolicy { final WindowState topApp = mPolicy.getTopFullscreenOpaqueWindow(); mStateController.onBarControlTargetChanged( statusControlTarget, statusControlTarget == mDummyControlTarget statusControlTarget == mTransientControlTarget ? getStatusControlTarget(focusedWin, true /* fake */) : statusControlTarget == notificationShade ? getStatusControlTarget(topApp, true /* fake */) : null, navControlTarget, navControlTarget == mDummyControlTarget navControlTarget == mTransientControlTarget ? getNavControlTarget(focusedWin, true /* fake */) : navControlTarget == notificationShade ? getNavControlTarget(topApp, true /* fake */) Loading Loading @@ -200,17 +167,17 @@ class InsetsPolicy { mFocusedWin, (showingTransientTypes & (Type.statusBars() | Type.navigationBars())) != 0, isGestureOnSystemBar); // The leashes can be created while updating bar control target. The surface transaction // of the new leashes might not be applied yet. The callback posted here ensures we can // get the valid leashes because the surface transaction will be applied in the next // animation frame which will be triggered if a new leash is created. mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> { synchronized (mDisplayContent.mWmService.mGlobalLock) { startAnimation(true /* show */, null /* callback */); } }); } @VisibleForTesting InsetsControlTarget getTransientControlTarget() { return mTransientControlTarget; } @VisibleForTesting InsetsControlTarget getPermanentControlTarget() { return mPermanentControlTarget; } void hideTransient() { Loading @@ -223,24 +190,9 @@ class InsetsPolicy { /* areVisible= */ false, /* wereRevealedFromSwipeOnSystemBar= */ false); startAnimation(false /* show */, () -> { synchronized (mDisplayContent.mWmService.mGlobalLock) { final SparseArray<InsetsSourceProvider> providers = mStateController.getSourceProviders(); for (int i = providers.size() - 1; i >= 0; i--) { final InsetsSourceProvider provider = providers.valueAt(i); if (!isTransient(provider.getSource().getType())) { continue; } // We are about to clear mShowingTransientTypes, we don't want the transient bar // can cause insets on the client. Restore the client visibility. provider.setClientVisible(false); } mShowingTransientTypes = 0; updateBarControlTarget(mFocusedWin); } }); } boolean isTransient(@InsetsType int type) { return (mShowingTransientTypes & type) != 0; Loading Loading @@ -502,7 +454,7 @@ class InsetsPolicy { private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin, boolean fake) { if (!fake && isTransient(Type.statusBars())) { return mDummyControlTarget; return mTransientControlTarget; } final WindowState notificationShade = mPolicy.getNotificationShade(); if (focusedWin == notificationShade) { Loading @@ -519,13 +471,13 @@ class InsetsPolicy { if (areTypesForciblyShowing(Type.statusBars())) { // Status bar is forcibly shown. We don't want the client to control the status bar, and // we will dispatch the real visibility of status bar to the client. return null; return mPermanentControlTarget; } if (forceShowsStatusBarTransiently() && !fake) { // Status bar is forcibly shown transiently, and its new visibility won't be // dispatched to the client so that we can keep the layout stable. We will dispatch the // fake control to the client, so that it can re-show the bar during this scenario. return mDummyControlTarget; return mTransientControlTarget; } if (!canBeTopFullscreenOpaqueWindow(focusedWin) && mPolicy.topAppHidesSystemBar(Type.statusBars()) Loading Loading @@ -556,7 +508,7 @@ class InsetsPolicy { return null; } if (!fake && isTransient(Type.navigationBars())) { return mDummyControlTarget; return mTransientControlTarget; } if (focusedWin == mPolicy.getNotificationShade()) { // Notification shade has control anyways, no reason to force anything. Loading @@ -579,13 +531,13 @@ class InsetsPolicy { if (areTypesForciblyShowing(Type.navigationBars())) { // Navigation bar is forcibly shown. We don't want the client to control the navigation // bar, and we will dispatch the real visibility of navigation bar to the client. return null; return mPermanentControlTarget; } if (forceShowsNavigationBarTransiently() && !fake) { // Navigation bar is forcibly shown transiently, and its new visibility won't be // dispatched to the client so that we can keep the layout stable. We will dispatch the // fake control to the client, so that it can re-show the bar during this scenario. return mDummyControlTarget; return mTransientControlTarget; } final WindowState notificationShade = mPolicy.getNotificationShade(); if (!canBeTopFullscreenOpaqueWindow(focusedWin) Loading Loading @@ -662,34 +614,6 @@ class InsetsPolicy { && (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; } @VisibleForTesting void startAnimation(boolean show, Runnable callback) { @InsetsType int typesReady = 0; final SparseArray<InsetsSourceControl> controlsReady = new SparseArray<>(); final InsetsSourceControl[] controls = mStateController.getControlsForDispatch(mDummyControlTarget); if (controls == null) { if (callback != null) { DisplayThread.getHandler().post(callback); } return; } for (InsetsSourceControl control : controls) { if (isTransient(control.getType()) && control.getLeash() != null) { typesReady |= control.getType(); controlsReady.put(control.getId(), new InsetsSourceControl(control)); } } controlAnimationUnchecked(typesReady, controlsReady, show, callback); } private void controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) { InsetsPolicyAnimationControlListener listener = new InsetsPolicyAnimationControlListener(show, callback, typesReady); listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show); } private void dispatchTransientSystemBarsVisibilityChanged( @Nullable WindowState focusedWindow, boolean areVisible, Loading Loading @@ -760,105 +684,138 @@ class InsetsPolicy { } } private class InsetsPolicyAnimationControlListener extends InsetsController.InternalAnimationControlListener { Runnable mFinishCallback; InsetsPolicyAnimationControlCallbacks mControlCallbacks; private static class ControlTarget implements InsetsControlTarget { InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) { super(show, false /* hasCallbacks */, types, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, false /* disable */, 0 /* floatingImeBottomInsets */, null /* loggingListener */, null /* jankContext */); mFinishCallback = finishCallback; mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this); private final InsetsState mState = new InsetsState(); private final InsetsController mInsetsController; private final InsetsStateController mStateController; private final String mName; ControlTarget(InsetsStateController stateController, Handler handler, String name) { mStateController = stateController; mInsetsController = new InsetsController(new Host(handler, name)); mName = name; } @Override protected void onAnimationFinish() { super.onAnimationFinish(); if (mFinishCallback != null) { DisplayThread.getHandler().post(mFinishCallback); public void notifyInsetsControlChanged() { mState.set(mStateController.getRawInsetsState(), true /* copySources */); mInsetsController.onStateChanged(mState); mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this)); } @Override public String toString() { return mName; } } private class InsetsPolicyAnimationControlCallbacks implements InsetsAnimationControlCallbacks { private InsetsAnimationControlImpl mAnimationControl = null; private InsetsPolicyAnimationControlListener mListener; private static class Host implements InsetsController.Host { InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) { mListener = listener; private final float[] mTmpFloat9 = new float[9]; private final Handler mHandler; private final String mName; Host(Handler handler, String name) { mHandler = handler; mName = name; } private void controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show) { if (typesReady == 0) { // nothing to animate. return; @Override public Handler getHandler() { return mHandler; } mAnimatingShown = show; final InsetsState state = mFocusedWin.getInsetsState(); @Override public void notifyInsetsChanged() { } // We are about to playing the default animation. Passing a null frame indicates // the controlled types should be animated regardless of the frame. mAnimationControl = new InsetsAnimationControlImpl(controls, null /* frame */, state, mListener, typesReady, this, mListener.getDurationMs(), getInsetsInterpolator(), show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, null /* translator */, null /* statsToken */); SurfaceAnimationThread.getHandler().post( () -> mListener.onReady(mAnimationControl, typesReady)); } @Override public void dispatchWindowInsetsAnimationPrepare( @NonNull WindowInsetsAnimation animation) { } /** Called on SurfaceAnimationThread without global WM lock held. */ @Override public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { if (mAnimationControl.applyChangeInsets(null /* outState */)) { mAnimationControl.finish(mAnimatingShown); } public Bounds dispatchWindowInsetsAnimationStart( @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds) { return bounds; } @Override public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) { // Nothing's needed here. Finish steps is handled in the listener // onAnimationFinished callback. public WindowInsets dispatchWindowInsetsAnimationProgress( @NonNull WindowInsets insets, @NonNull List<WindowInsetsAnimation> runningAnimations) { return insets; } /** Called on SurfaceAnimationThread without global WM lock held. */ @Override public void applySurfaceParams( final SyncRtSurfaceTransactionApplier.SurfaceParams... params) { SurfaceControl.Transaction t = new SurfaceControl.Transaction(); for (int i = params.length - 1; i >= 0; i--) { SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i]; applyParams(t, surfaceParams, mTmpFloat9); public void dispatchWindowInsetsAnimationEnd( @NonNull WindowInsetsAnimation animation) { } @Override public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... p) { final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); for (int i = p.length - 1; i >= 0; i--) { SyncRtSurfaceTransactionApplier.applyParams(t, p[i], mTmpFloat9); } t.apply(); t.close(); } // Since we don't push applySurfaceParams to a Handler-queue we don't need // to push release in this case. @Override public void releaseSurfaceControlFromRt(SurfaceControl sc) { sc.release(); public void updateRequestedVisibleTypes(int types) { } @Override public boolean hasAnimationCallbacks() { return false; } @Override public <T extends InsetsAnimationControlRunner & InternalInsetsAnimationController> void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds) { public void setSystemBarsAppearance(int appearance, int mask) { } @Override public int getSystemBarsAppearance() { return 0; } @Override public void reportPerceptible(int types, boolean perceptible) { // No-op for now - only client windows report perceptibility for now, with policy // controllers assumed to always be perceptible. public void setSystemBarsBehavior(int behavior) { } @Override public int getSystemBarsBehavior() { return BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; } @Override public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) { surfaceControl.release(); } @Override public void addOnPreDrawRunnable(Runnable r) { } @Override public void postInsetsAnimationCallback(Runnable r) { } @Override public InputMethodManager getInputMethodManager() { return null; } @Nullable @Override public String getRootViewTitle() { return mName; } @Override public int dipToPx(int dips) { return 0; } @Nullable @Override public IBinder getWindowToken() { return null; } } }
services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +0 −13 Original line number Diff line number Diff line Loading @@ -31,17 +31,12 @@ import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; import android.app.StatusBarManager; Loading Loading @@ -268,8 +263,6 @@ public class InsetsPolicyTest extends WindowTestsBase { navBar.setHasSurface(true); navBarProvider.setServerVisible(true); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); spyOn(policy); doNothing().when(policy).startAnimation(anyBoolean(), any()); // Make both system bars invisible. mAppWindow.setRequestedVisibleTypes( Loading Loading @@ -305,8 +298,6 @@ public class InsetsPolicyTest extends WindowTestsBase { addNavigationBar().getControllableInsetProvider().setServerVisible(true); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); spyOn(policy); doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(mAppWindow); policy.showTransient(navigationBars() | statusBars(), true /* isGestureOnSystemBar */); Loading Loading @@ -341,8 +332,6 @@ public class InsetsPolicyTest extends WindowTestsBase { mAppWindow.mAboveInsetsState.addSource(navBarSource); mAppWindow.mAboveInsetsState.addSource(statusBarSource); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); spyOn(policy); doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(mAppWindow); policy.showTransient(navigationBars() | statusBars(), true /* isGestureOnSystemBar */); Loading Loading @@ -390,8 +379,6 @@ public class InsetsPolicyTest extends WindowTestsBase { final WindowState app2 = addWindow(TYPE_APPLICATION, "app"); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); spyOn(policy); doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(app); policy.showTransient(navigationBars() | statusBars(), true /* isGestureOnSystemBar */); Loading
services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +4 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,10 @@ class TestDisplayContent extends DisplayContent { spyOn(inputMonitor); doNothing().when(inputMonitor).resumeDispatchingLw(any()); final InsetsPolicy insetsPolicy = getInsetsPolicy(); WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget()); WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getTransientControlTarget()); // For devices that set the sysprop ro.bootanim.set_orientation_<display_id> // See DisplayRotation#readDefaultDisplayRotation for context. // Without that, meaning of height and width in context of the tests can be swapped if Loading
services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +13 −0 Original line number Diff line number Diff line Loading @@ -222,6 +222,10 @@ class WindowTestsBase extends SystemServiceTestsBase { displayPolicy.finishWindowsDrawn(); displayPolicy.finishScreenTurningOn(); final InsetsPolicy insetsPolicy = mDefaultDisplay.getInsetsPolicy(); suppressInsetsAnimation(insetsPolicy.getTransientControlTarget()); suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget()); mTransaction = mSystemServicesTestRule.mTransaction; mMockSession = mock(Session.class); Loading Loading @@ -278,6 +282,15 @@ class WindowTestsBase extends SystemServiceTestsBase { checkDeviceSpecificOverridesNotApplied(); } /** * The test doesn't create real SurfaceControls, but mocked ones. This prevents the target from * controlling them, or it will cause {@link NullPointerException}. */ static void suppressInsetsAnimation(InsetsControlTarget target) { spyOn(target); Mockito.doNothing().when(target).notifyInsetsControlChanged(); } @After public void tearDown() throws Exception { if (mUseFakeSettingsProvider) { Loading