diff options
author | Jorim Jaggi <jjaggi@google.com> | 2020-04-06 14:15:46 +0200 |
---|---|---|
committer | Jorim Jaggi <jjaggi@google.com> | 2020-04-06 14:28:59 +0200 |
commit | 33a2183d4b26e4ad72a2cbe5b01bfe6da5807a26 (patch) | |
tree | ca35336f98521243ea8f691082c125f7722fec9b | |
parent | 25e19732fc31f809b39019439c124518bbdf72df (diff) |
Revert "Revert "Defer updating InsetsSource.mFrame while animating""
This reverts commit 8c56ac6b945e4050d56968e2adac673e052b772d.
Underlying issue has been fixed:
Test: InsetsControllerTest
Bug: 152071027
Change-Id: I2b80de7067bf688a6b36233b9a1e92e2cf31c148
4 files changed, 123 insertions, 57 deletions
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 72ddaca27a76..43c7bede38c2 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -18,6 +18,7 @@ package android.view; import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_IME; +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; @@ -471,7 +472,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (!localStateChanged && mLastDispachedState.equals(state)) { return false; } - mState.set(state); + updateState(state); mLastDispachedState.set(state, true /* copySources */); applyLocalVisibilityOverride(); if (localStateChanged) { @@ -480,11 +481,25 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (!mState.equals(mLastDispachedState, true /* excludingCaptionInsets */)) { sendStateToWindowManager(); } + return true; + } + + private void updateState(InsetsState newState) { + mState.setDisplayFrame(newState.getDisplayFrame()); + for (int i = newState.getSourcesCount() - 1; i >= 0; i--) { + InsetsSource source = newState.sourceAt(i); + getSourceConsumer(source.getType()).updateSource(source); + } + for (int i = mState.getSourcesCount() - 1; i >= 0; i--) { + InsetsSource source = mState.sourceAt(i); + if (newState.peekSource(source.getType()) == null) { + mState.removeSource(source.getType()); + } + } if (mCaptionInsetsHeight != 0) { mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top, mFrame.right, mFrame.top + mCaptionInsetsHeight)); } - return true; } private boolean captionInsetsUnchanged() { @@ -879,8 +894,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation control.cancel(); } for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { - if (mRunningAnimations.get(i).runner == control) { + RunningAnimation runningAnimation = mRunningAnimations.get(i); + if (runningAnimation.runner == control) { mRunningAnimations.remove(i); + ArraySet<Integer> types = toInternalType(control.getTypes()); + for (int j = types.size() - 1; j >= 0; j--) { + if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) { + mViewRoot.notifyInsetsChanged(); + } + } break; } } diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index f74221dc3e3a..69bab4df2cae 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -16,11 +16,13 @@ package android.view; +import static android.view.InsetsController.ANIMATION_TYPE_NONE; import static android.view.InsetsController.AnimationType; import static android.view.InsetsState.toPublicType; import android.annotation.IntDef; import android.annotation.Nullable; +import android.graphics.Rect; import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type.InsetsType; @@ -64,6 +66,8 @@ public class InsetsSourceConsumer { private final Supplier<Transaction> mTransactionSupplier; private @Nullable InsetsSourceControl mSourceControl; private boolean mHasWindowFocus; + private Rect mPendingFrame; + private Rect mPendingVisibleFrame; public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state, Supplier<Transaction> transactionSupplier, InsetsController controller) { @@ -215,6 +219,38 @@ public class InsetsSourceConsumer { // no-op for types that always return ShowResult#SHOW_IMMEDIATELY. } + void updateSource(InsetsSource newSource) { + InsetsSource source = mState.peekSource(mType); + if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE + || source.getFrame().equals(newSource.getFrame())) { + mState.addSource(newSource); + return; + } + + // Frame is changing while animating. Keep note of the new frame but keep existing frame + // until animaition is finished. + newSource = new InsetsSource(newSource); + mPendingFrame = new Rect(newSource.getFrame()); + mPendingVisibleFrame = newSource.getVisibleFrame() != null + ? new Rect(newSource.getVisibleFrame()) + : null; + newSource.setFrame(source.getFrame()); + newSource.setVisibleFrame(source.getVisibleFrame()); + mState.addSource(newSource); + } + + boolean notifyAnimationFinished() { + if (mPendingFrame != null) { + InsetsSource source = mState.getSource(mType); + source.setFrame(mPendingFrame); + source.setVisibleFrame(mPendingVisibleFrame); + mPendingFrame = null; + mPendingVisibleFrame = null; + return true; + } + return false; + } + /** * Sets requested visibility from the client, regardless of whether we are able to control it at * the moment. diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index b9c71a2a207c..cbb379bf8207 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -32,6 +32,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -638,7 +639,31 @@ public class InsetsControllerTest { }); } + @Test + public void testFrameUpdateDuringAnimation() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + + mController.onControlsChanged(createSingletonControl(ITYPE_IME)); + // Pretend IME is calling + mController.show(ime(), true /* fromIme */); + + InsetsState copy = new InsetsState(mController.getState(), true /* copySources */); + copy.getSource(ITYPE_IME).setFrame(0, 1, 2, 3); + copy.getSource(ITYPE_IME).setVisibleFrame(new Rect(4, 5, 6, 7)); + mController.onStateChanged(copy); + assertNotEquals(new Rect(0, 1, 2, 3), + mController.getState().getSource(ITYPE_IME).getFrame()); + assertNotEquals(new Rect(4, 5, 6, 7), + mController.getState().getSource(ITYPE_IME).getVisibleFrame()); + mController.cancelExistingAnimation(); + assertEquals(new Rect(0, 1, 2, 3), + mController.getState().getSource(ITYPE_IME).getFrame()); + assertEquals(new Rect(4, 5, 6, 7), + mController.getState().getSource(ITYPE_IME).getVisibleFrame()); + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } @Test public void testCaptionInsetsStateAssemble() { diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java index 548af0c54b03..498cb7c1c710 100644 --- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java +++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java @@ -18,7 +18,6 @@ package com.google.android.test.windowinsetstests; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; - import static java.lang.Math.max; import static java.lang.Math.min; @@ -31,6 +30,7 @@ import android.content.Context; import android.graphics.Insets; import android.os.Bundle; import android.util.AttributeSet; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; @@ -44,11 +44,11 @@ import android.view.WindowInsetsController.OnControllableInsetsChangedListener; import android.view.animation.LinearInterpolator; import android.widget.LinearLayout; -import androidx.appcompat.app.AppCompatActivity; - import java.util.ArrayList; import java.util.List; +import androidx.appcompat.app.AppCompatActivity; + public class WindowInsetsActivity extends AppCompatActivity { private View mRoot; @@ -191,6 +191,40 @@ public class WindowInsetsActivity extends AppCompatActivity { mTransitions.forEach(it -> it.onFinish(animation)); } }); + + findViewById(R.id.floating_action_button).setOnClickListener( + v -> v.getWindowInsetsController().controlWindowInsetsAnimation(ime(), -1, + new LinearInterpolator(), null /* cancellationSignal */, + new WindowInsetsAnimationControlListener() { + @Override + public void onReady( + WindowInsetsAnimationController controller, + int types) { + ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + anim.setDuration(1500); + anim.addUpdateListener(animation + -> controller.setInsetsAndAlpha( + controller.getShownStateInsets(), + (float) animation.getAnimatedValue(), + anim.getAnimatedFraction())); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + controller.finish(true); + } + }); + anim.start(); + } + + @Override + public void onCancelled(WindowInsetsAnimationController controller) { + } + + @Override + public void onFinished(WindowInsetsAnimationController controller) { + } + })); } @Override @@ -200,57 +234,6 @@ public class WindowInsetsActivity extends AppCompatActivity { getWindow().getDecorView().post(() -> getWindow().setDecorFitsSystemWindows(false)); } - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - getWindow().getInsetsController().addOnControllableInsetsChangedListener( - new OnControllableInsetsChangedListener() { - - boolean hasControl = false; - @Override - public void onControllableInsetsChanged(WindowInsetsController controller, - int types) { - if ((types & ime()) != 0 && !hasControl) { - hasControl = true; - controller.controlWindowInsetsAnimation(ime(), -1, - new LinearInterpolator(), null /* cancellationSignal */, - new WindowInsetsAnimationControlListener() { - @Override - public void onReady( - WindowInsetsAnimationController controller, - int types) { - ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); - anim.setDuration(1500); - anim.addUpdateListener(animation - -> controller.setInsetsAndAlpha( - controller.getShownStateInsets(), - (float) animation.getAnimatedValue(), - anim.getAnimatedFraction())); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - controller.finish(true); - } - }); - anim.start(); - } - - @Override - public void onFinished( - WindowInsetsAnimationController controller) { - } - - @Override - public void onCancelled( - WindowInsetsAnimationController controller) { - } - }); - } - } - }); - } - static class Transition { private int mEndBottom; private int mStartBottom; |