diff options
3 files changed, 97 insertions, 14 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 7809b638353b..44bb08711567 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -109,6 +109,10 @@ object Flags { @JvmField val NOTIFICATION_ANIMATE_BIG_PICTURE = unreleasedFlag(120, "notification_animate_big_picture") + @JvmField + val ANIMATED_NOTIFICATION_SHADE_INSETS = + unreleasedFlag(270682168, "animated_notification_shade_insets", teamfood = true) + // 200 - keyguard/lockscreen // ** Flag retired ** // public static final BooleanFlag KEYGUARD_LAYOUT = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index d2087ba6ca1c..977e1bb31049 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -67,6 +67,7 @@ import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; import android.view.WindowInsets; +import android.view.WindowInsetsAnimation; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AnimationUtils; @@ -199,6 +200,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private final boolean mDebugRemoveAnimation; private final boolean mSimplifiedAppearFraction; private final boolean mUseRoundnessSourceTypes; + private boolean mAnimatedInsets; private int mContentHeight; private float mIntrinsicContentHeight; @@ -207,7 +209,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private int mTopPadding; private boolean mAnimateNextTopPaddingChange; private int mBottomPadding; - private int mBottomInset = 0; + @VisibleForTesting + int mBottomInset = 0; private float mQsExpansionFraction; private final int mSplitShadeMinContentHeight; @@ -388,9 +391,33 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } }; + private boolean mPulsing; private boolean mScrollable; private View mForcedScroll; + private boolean mIsInsetAnimationRunning; + + private final WindowInsetsAnimation.Callback mInsetsCallback = + new WindowInsetsAnimation.Callback( + WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE) { + + @Override + public void onPrepare(WindowInsetsAnimation animation) { + mIsInsetAnimationRunning = true; + } + + @Override + public WindowInsets onProgress(WindowInsets windowInsets, + List<WindowInsetsAnimation> list) { + updateBottomInset(windowInsets); + return windowInsets; + } + + @Override + public void onEnd(WindowInsetsAnimation animation) { + mIsInsetAnimationRunning = false; + } + }; /** * @see #setHideAmount(float, float) @@ -584,6 +611,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION); mSimplifiedAppearFraction = featureFlags.isEnabled(Flags.SIMPLIFIED_APPEAR_FRACTION); mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES); + setAnimatedInsetsEnabled(featureFlags.isEnabled(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS)); mSectionsManager = Dependency.get(NotificationSectionsManager.class); mScreenOffAnimationController = Dependency.get(ScreenOffAnimationController.class); @@ -622,6 +650,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mGroupMembershipManager = Dependency.get(GroupMembershipManager.class); mGroupExpansionManager = Dependency.get(GroupExpansionManager.class); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); + if (mAnimatedInsets) { + setWindowInsetsAnimationCallback(mInsetsCallback); + } } /** @@ -690,6 +721,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @VisibleForTesting + void setAnimatedInsetsEnabled(boolean enabled) { + mAnimatedInsets = enabled; + } + + @VisibleForTesting @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void updateFooter() { if (mFooterView == null) { @@ -1781,7 +1817,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return; } mForcedScroll = v; - scrollTo(v); + if (mAnimatedInsets) { + updateForcedScroll(); + } else { + scrollTo(v); + } } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -1813,26 +1853,46 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable + ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding()); } + private void updateBottomInset(WindowInsets windowInsets) { + mBottomInset = windowInsets.getInsets(WindowInsets.Type.ime()).bottom; + + if (mForcedScroll != null) { + updateForcedScroll(); + } + + int range = getScrollRange(); + if (mOwnScrollY > range) { + setOwnScrollY(range); + } + } + @Override @ShadeViewRefactor(RefactorComponent.COORDINATOR) public WindowInsets onApplyWindowInsets(WindowInsets insets) { - mBottomInset = insets.getInsets(WindowInsets.Type.ime()).bottom; + if (!mAnimatedInsets) { + mBottomInset = insets.getInsets(WindowInsets.Type.ime()).bottom; + } mWaterfallTopInset = 0; final DisplayCutout cutout = insets.getDisplayCutout(); if (cutout != null) { mWaterfallTopInset = cutout.getWaterfallInsets().top; } - - int range = getScrollRange(); - if (mOwnScrollY > range) { - // HACK: We're repeatedly getting staggered insets here while the IME is - // animating away. To work around that we'll wait until things have settled. - removeCallbacks(mReclamp); - postDelayed(mReclamp, 50); - } else if (mForcedScroll != null) { - // The scroll was requested before we got the actual inset - in case we need - // to scroll up some more do so now. - scrollTo(mForcedScroll); + if (mAnimatedInsets && !mIsInsetAnimationRunning) { + // update bottom inset e.g. after rotation + updateBottomInset(insets); + } + if (!mAnimatedInsets) { + int range = getScrollRange(); + if (mOwnScrollY > range) { + // HACK: We're repeatedly getting staggered insets here while the IME is + // animating away. To work around that we'll wait until things have settled. + removeCallbacks(mReclamp); + postDelayed(mReclamp, 50); + } else if (mForcedScroll != null) { + // The scroll was requested before we got the actual inset - in case we need + // to scroll up some more do so now. + scrollTo(mForcedScroll); + } } return insets; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index dd7143ae7e16..cbf841b5a1f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.stack; import static android.view.View.GONE; +import static android.view.WindowInsets.Type.ime; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; @@ -46,6 +47,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.graphics.Insets; import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -54,6 +56,8 @@ import android.util.MathUtils; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.WindowInsets; +import android.view.WindowInsetsAnimation; import android.widget.TextView; import androidx.test.annotation.UiThreadTest; @@ -91,6 +95,8 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.ArrayList; + /** * Tests for {@link NotificationStackScrollLayout}. */ @@ -843,6 +849,19 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { verify(mEmptyShadeView).setFooterText(not(0)); } + @Test + public void testWindowInsetAnimationProgress_updatesBottomInset() { + int bottomImeInset = 100; + mStackScrollerInternal.setAnimatedInsetsEnabled(true); + WindowInsets windowInsets = new WindowInsets.Builder() + .setInsets(ime(), Insets.of(0, 0, 0, bottomImeInset)).build(); + ArrayList<WindowInsetsAnimation> windowInsetsAnimations = new ArrayList<>(); + mStackScrollerInternal + .dispatchWindowInsetsAnimationProgress(windowInsets, windowInsetsAnimations); + + assertEquals(bottomImeInset, mStackScrollerInternal.mBottomInset); + } + private void setBarStateForTest(int state) { // Can't inject this through the listener or we end up on the actual implementation // rather than the mock because the spy just coppied the anonymous inner /shruggie. |