Merge "Scroll NotificationStack in sync with IME when inline replying" into tm-qpr-dev am: e1766512a2

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21463764

Change-Id: I6bf86bfc9e49e16e2c3116575f2ffa4766b00472
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Johannes Gallmann
2023-03-06 11:17:53 +00:00
committed by Automerger Merge Worker
3 changed files with 97 additions and 14 deletions

View File

@@ -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 =

View File

@@ -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);
}
}
/**
@@ -689,6 +720,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mHasFilteredOutSeenNotifications = hasFilteredOutSeenNotifications;
}
@VisibleForTesting
void setAnimatedInsetsEnabled(boolean enabled) {
mAnimatedInsets = enabled;
}
@VisibleForTesting
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateFooter() {
@@ -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;
}

View File

@@ -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.