Loading core/java/android/view/InsetsController.java +38 −18 Original line number Diff line number Diff line Loading @@ -22,8 +22,6 @@ import static android.view.InsetsState.toInternalType; import static android.view.InsetsState.toPublicType; import static android.view.WindowInsets.Type.all; import static android.view.WindowInsets.Type.ime; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; import android.animation.AnimationHandler; import android.animation.Animator; Loading @@ -43,7 +41,6 @@ import android.util.SparseArray; import android.view.InsetsSourceConsumer.ShowResult; import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl.Transaction; import android.view.ViewTreeObserver.OnPreDrawListener; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation.Bounds; Loading Loading @@ -424,8 +421,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private final String TAG = "InsetsControllerImpl"; /** The local state */ private final InsetsState mState = new InsetsState(); private final InsetsState mLastDispachedState = new InsetsState(); /** The state dispatched from server */ private final InsetsState mLastDispatchedState = new InsetsState(); /** The state sent to server */ private final InsetsState mRequestedState = new InsetsState(); private final Rect mFrame = new Rect(); private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator; Loading Loading @@ -539,24 +542,24 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } public InsetsState getLastDispatchedState() { return mLastDispachedState; return mLastDispatchedState; } @VisibleForTesting public boolean onStateChanged(InsetsState state) { boolean localStateChanged = !mState.equals(state, true /* excludingCaptionInsets */) || !captionInsetsUnchanged(); if (!localStateChanged && mLastDispachedState.equals(state)) { if (!localStateChanged && mLastDispatchedState.equals(state)) { return false; } updateState(state); mLastDispachedState.set(state, true /* copySources */); mLastDispatchedState.set(state, true /* copySources */); applyLocalVisibilityOverride(); if (localStateChanged) { mHost.notifyInsetsChanged(); } if (!mState.equals(mLastDispachedState, true /* excludingCaptionInsets */)) { sendStateToWindowManager(); if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */)) { updateRequestedState(); } return true; } Loading Loading @@ -629,8 +632,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } int[] showTypes = new int[1]; int[] hideTypes = new int[1]; final boolean hasControl = mTmpControlArray.size() > 0; final int[] showTypes = new int[1]; final int[] hideTypes = new int[1]; // Ensure to update all existing source consumers for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { Loading Loading @@ -663,6 +667,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (hideTypes[0] != 0) { applyAnimation(hideTypes[0], false /* show */, false /* fromIme */); } if (hasControl) { // We might have changed our requested visibilities while we don't have the control, // so we need to update our requested state once we have control. Otherwise, our // requested state at the server side might be incorrect. updateRequestedState(); } } @Override Loading Loading @@ -992,7 +1002,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting public void notifyVisibilityChanged() { mHost.notifyInsetsChanged(); sendStateToWindowManager(); updateRequestedState(); } /** Loading Loading @@ -1042,18 +1052,28 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } /** * Sends the local visibility state back to window manager. * Sends the local visibility state back to window manager if it is changed. */ private void sendStateToWindowManager() { InsetsState tmpState = new InsetsState(); private void updateRequestedState() { boolean changed = false; for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); if (consumer.getType() == ITYPE_CAPTION_BAR) continue; final @InternalInsetsType int type = consumer.getType(); if (type == ITYPE_CAPTION_BAR) { continue; } if (consumer.getControl() != null) { tmpState.addSource(mState.getSource(consumer.getType())); final InsetsSource localSource = mState.getSource(type); if (!localSource.equals(mRequestedState.peekSource(type))) { mRequestedState.addSource(new InsetsSource(localSource)); changed = true; } } } mHost.onInsetsModified(tmpState); if (!changed) { return; } mHost.onInsetsModified(mRequestedState); } @VisibleForTesting Loading core/tests/coretests/src/android/view/InsetsControllerTest.java +40 −2 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ public class InsetsControllerTest { private SurfaceSession mSession = new SurfaceSession(); private SurfaceControl mLeash; private ViewRootImpl mViewRoot; private TestHost mTestHost; private TestHandler mTestHandler; private OffsettableClock mTestClock; private static InsetsModeSession sInsetsModeSession; Loading Loading @@ -123,8 +124,8 @@ public class InsetsControllerTest { } mTestClock = new OffsettableClock(); mTestHandler = new TestHandler(null, mTestClock); mController = new InsetsController(new ViewRootInsetsControllerHost(mViewRoot), (controller, type) -> { mTestHost = new TestHost(mViewRoot); mController = new InsetsController(mTestHost, (controller, type) -> { if (type == ITYPE_IME) { return new InsetsSourceConsumer(type, controller.getState(), Transaction::new, controller) { Loading Loading @@ -686,6 +687,24 @@ public class InsetsControllerTest { }); } @Test public void testRequestedState() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); mController.hide(statusBars()); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); mController.onControlsChanged(new InsetsSourceControl[0]); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); InsetsState newState = new InsetsState(mController.getState(), true /* copySource */); mController.onStateChanged(newState); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); mController.show(statusBars()); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); assertTrue(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); }); } private void waitUntilNextFrame() throws Exception { final CountDownLatch latch = new CountDownLatch(1); Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT, Loading Loading @@ -717,4 +736,23 @@ public class InsetsControllerTest { mController.onControlsChanged(controls); return controls; } private static class TestHost extends ViewRootInsetsControllerHost { private InsetsState mModifiedState = new InsetsState(); TestHost(ViewRootImpl viewRoot) { super(viewRoot); } @Override public void onInsetsModified(InsetsState insetsState) { mModifiedState = new InsetsState(insetsState, true /* copySource */); super.onInsetsModified(insetsState); } public InsetsState getModifiedState() { return mModifiedState; } } } Loading
core/java/android/view/InsetsController.java +38 −18 Original line number Diff line number Diff line Loading @@ -22,8 +22,6 @@ import static android.view.InsetsState.toInternalType; import static android.view.InsetsState.toPublicType; import static android.view.WindowInsets.Type.all; import static android.view.WindowInsets.Type.ime; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; import android.animation.AnimationHandler; import android.animation.Animator; Loading @@ -43,7 +41,6 @@ import android.util.SparseArray; import android.view.InsetsSourceConsumer.ShowResult; import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl.Transaction; import android.view.ViewTreeObserver.OnPreDrawListener; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation.Bounds; Loading Loading @@ -424,8 +421,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private final String TAG = "InsetsControllerImpl"; /** The local state */ private final InsetsState mState = new InsetsState(); private final InsetsState mLastDispachedState = new InsetsState(); /** The state dispatched from server */ private final InsetsState mLastDispatchedState = new InsetsState(); /** The state sent to server */ private final InsetsState mRequestedState = new InsetsState(); private final Rect mFrame = new Rect(); private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator; Loading Loading @@ -539,24 +542,24 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } public InsetsState getLastDispatchedState() { return mLastDispachedState; return mLastDispatchedState; } @VisibleForTesting public boolean onStateChanged(InsetsState state) { boolean localStateChanged = !mState.equals(state, true /* excludingCaptionInsets */) || !captionInsetsUnchanged(); if (!localStateChanged && mLastDispachedState.equals(state)) { if (!localStateChanged && mLastDispatchedState.equals(state)) { return false; } updateState(state); mLastDispachedState.set(state, true /* copySources */); mLastDispatchedState.set(state, true /* copySources */); applyLocalVisibilityOverride(); if (localStateChanged) { mHost.notifyInsetsChanged(); } if (!mState.equals(mLastDispachedState, true /* excludingCaptionInsets */)) { sendStateToWindowManager(); if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */)) { updateRequestedState(); } return true; } Loading Loading @@ -629,8 +632,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } int[] showTypes = new int[1]; int[] hideTypes = new int[1]; final boolean hasControl = mTmpControlArray.size() > 0; final int[] showTypes = new int[1]; final int[] hideTypes = new int[1]; // Ensure to update all existing source consumers for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { Loading Loading @@ -663,6 +667,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (hideTypes[0] != 0) { applyAnimation(hideTypes[0], false /* show */, false /* fromIme */); } if (hasControl) { // We might have changed our requested visibilities while we don't have the control, // so we need to update our requested state once we have control. Otherwise, our // requested state at the server side might be incorrect. updateRequestedState(); } } @Override Loading Loading @@ -992,7 +1002,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting public void notifyVisibilityChanged() { mHost.notifyInsetsChanged(); sendStateToWindowManager(); updateRequestedState(); } /** Loading Loading @@ -1042,18 +1052,28 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } /** * Sends the local visibility state back to window manager. * Sends the local visibility state back to window manager if it is changed. */ private void sendStateToWindowManager() { InsetsState tmpState = new InsetsState(); private void updateRequestedState() { boolean changed = false; for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); if (consumer.getType() == ITYPE_CAPTION_BAR) continue; final @InternalInsetsType int type = consumer.getType(); if (type == ITYPE_CAPTION_BAR) { continue; } if (consumer.getControl() != null) { tmpState.addSource(mState.getSource(consumer.getType())); final InsetsSource localSource = mState.getSource(type); if (!localSource.equals(mRequestedState.peekSource(type))) { mRequestedState.addSource(new InsetsSource(localSource)); changed = true; } } } mHost.onInsetsModified(tmpState); if (!changed) { return; } mHost.onInsetsModified(mRequestedState); } @VisibleForTesting Loading
core/tests/coretests/src/android/view/InsetsControllerTest.java +40 −2 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ public class InsetsControllerTest { private SurfaceSession mSession = new SurfaceSession(); private SurfaceControl mLeash; private ViewRootImpl mViewRoot; private TestHost mTestHost; private TestHandler mTestHandler; private OffsettableClock mTestClock; private static InsetsModeSession sInsetsModeSession; Loading Loading @@ -123,8 +124,8 @@ public class InsetsControllerTest { } mTestClock = new OffsettableClock(); mTestHandler = new TestHandler(null, mTestClock); mController = new InsetsController(new ViewRootInsetsControllerHost(mViewRoot), (controller, type) -> { mTestHost = new TestHost(mViewRoot); mController = new InsetsController(mTestHost, (controller, type) -> { if (type == ITYPE_IME) { return new InsetsSourceConsumer(type, controller.getState(), Transaction::new, controller) { Loading Loading @@ -686,6 +687,24 @@ public class InsetsControllerTest { }); } @Test public void testRequestedState() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); mController.hide(statusBars()); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); mController.onControlsChanged(new InsetsSourceControl[0]); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); InsetsState newState = new InsetsState(mController.getState(), true /* copySource */); mController.onStateChanged(newState); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); mController.show(statusBars()); assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); assertTrue(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); }); } private void waitUntilNextFrame() throws Exception { final CountDownLatch latch = new CountDownLatch(1); Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT, Loading Loading @@ -717,4 +736,23 @@ public class InsetsControllerTest { mController.onControlsChanged(controls); return controls; } private static class TestHost extends ViewRootInsetsControllerHost { private InsetsState mModifiedState = new InsetsState(); TestHost(ViewRootImpl viewRoot) { super(viewRoot); } @Override public void onInsetsModified(InsetsState insetsState) { mModifiedState = new InsetsState(insetsState, true /* copySource */); super.onInsetsModified(insetsState); } public InsetsState getModifiedState() { return mModifiedState; } } }