Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +65 −0 Original line number Diff line number Diff line Loading @@ -2056,6 +2056,69 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { assertThat(mStackScroller.getOwnScrollY()).isEqualTo(100 - childHeight); } @Test @EnableSceneContainer public void testRequestScrollToRemoteInput_singleRow() { final Consumer<Float> scroller = mock(FloatConsumer.class); mStackScroller.setRemoteInputRowBottomBoundConsumer(scroller); // GIVEN a non-grouped notification row with specific dimensions and offsets ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); when(row.getTranslationY()).thenReturn(100f); when(row.getActualHeight()).thenReturn(200); when(row.getRemoteInputActionsContainerExpandedOffset()).thenReturn(50f); mTestableResources.addOverride( com.android.internal.R.dimen.notification_content_margin, 10); // WHEN requestScrollToRemoteInput is called mStackScroller.requestScrollToRemoteInput(row); // THEN the correct bottom bound is sent // expected = translationY + height + remoteInputOffset + contentMargin // expected = 100 + 200 + 50 + 10 = 360 verify(scroller).accept(360f); // WHEN requestScrollToRemoteInput is called with null mStackScroller.requestScrollToRemoteInput(null); // THEN null is sent verify(scroller).accept(null); } @Test @EnableSceneContainer public void testRequestScrollToRemoteInput_childInGroup() { final Consumer<Float> scroller = mock(FloatConsumer.class); mStackScroller.setRemoteInputRowBottomBoundConsumer(scroller); // GIVEN a grouped notification row ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class); ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); when(row.getEntryAdapter()).thenReturn(mock(EntryAdapter.class)); when(mGroupMembershipManger.isChildInGroup(row.getEntryAdapter())).thenReturn(true); when(row.getTranslationY()).thenReturn(100f); when(row.getActualHeight()).thenReturn(200); when(row.getRemoteInputActionsContainerExpandedOffset()).thenReturn(50f); when(parent.getTranslationY()).thenReturn(50f); when(row.getNotificationParent()).thenReturn(parent); mTestableResources.addOverride( com.android.internal.R.dimen.notification_content_margin, 10); // WHEN requestScrollToRemoteInput is called for the grouped row mStackScroller.requestScrollToRemoteInput(row); // THEN the correct bottom bound is sent, including the parent's translation // expected = parentTranslationY + translationY + height + remoteInputOffset + contentMargin // expected = 50 + 100 + 200 + 50 + 10 = 410 verify(scroller).accept(410f); // WHEN requestScrollToRemoteInput is called with null mStackScroller.requestScrollToRemoteInput(null); // THEN null is sent verify(scroller).accept(null); } private MotionEvent captureTouchSentToSceneFramework() { ArgumentCaptor<MotionEvent> captor = ArgumentCaptor.forClass(MotionEvent.class); verify(mStackScrollLayoutController).sendTouchToSceneFramework(captor.capture()); Loading Loading @@ -2150,6 +2213,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { private abstract static class BooleanConsumer implements Consumer<Boolean> { } private abstract static class FloatConsumer implements Consumer<Float> { } private ShadeScrimShape createScrimShape(int left, int top, int right, int bottom) { ShadeScrimBounds bounds = new ShadeScrimBounds(left, top, right, bottom); return new ShadeScrimShape(bounds, 0, 0); Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +31 −5 Original line number Diff line number Diff line Loading @@ -766,15 +766,41 @@ public class NotificationStackScrollLayout mSectionsManager.reinflateViews(); } void sendRemoteInputRowBottomBound(Float bottom) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; if (bottom != null) { bottom += getResources().getDimensionPixelSize( com.android.internal.R.dimen.notification_content_margin); /** * Requests to scroll to the RemoteInput view of the given row. * @param row currently holding the active RemoteInput, or null if the RemoteInput is inactive. */ void requestScrollToRemoteInput(@Nullable ExpandableNotificationRow row) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { return; } // When SceneContainer is enabled, the NSSL doesn't handle its own scrolling. // Instead, it calculates the required scroll amount and emits it as a "synthetic scroll" // event. The Compose-based UI (`NotificationScrollingStack.kt`) consumes this event // and performs the actual scroll, synchronizing it with the IME animation. Float bottom = (row != null) ? getRemoteInputViewBottom(row) : null; mScrollViewFields.sendRemoteInputRowBottomBound(bottom); } /** * Calculates the Y position of the bottom of the remoteInput view, relative to the NSSL. * * @param row with an active remoteInput * @return The bottom Y position of the view relative to this layout. */ private float getRemoteInputViewBottom(ExpandableNotificationRow row) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f; // Calculate the base top position float topPosition = row.getTranslationY() // For nested notifications, add the parent's translation. + (isChildInGroup(row) ? row.getNotificationParent().getTranslationY() : 0f); int height = row.getActualHeight(); float remoteInputOffset = row.getRemoteInputActionsContainerExpandedOffset(); float contentMargin = getResources().getDimensionPixelSize( com.android.internal.R.dimen.notification_content_margin); return topPosition + height + remoteInputOffset + contentMargin; } void updateBgColor() { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +6 −7 Original line number Diff line number Diff line Loading @@ -1670,28 +1670,27 @@ public class NotificationStackScrollLayoutController implements Dumpable { public void setRemoteInputActive(RemoteInputEntryAdapter entry, boolean remoteInputActive) { if (SceneContainerFlag.isEnabled()) { sendRemoteInputRowBottomBound(entry, remoteInputActive); setRemoteInputActiveRow(entry, remoteInputActive); } entry.setRemoteInputActive(mHeadsUpManager, remoteInputActive); entry.notifyHeightChanged(true /* needsAnimation */); } public void lockScrollTo(ExpandableNotificationRow row) { if (!SceneContainerFlag.isEnabled()) { mView.lockScrollTo(row); } } public void requestDisallowLongPressAndDismiss() { mView.requestDisallowLongPress(); mView.requestDisallowDismiss(); } private void sendRemoteInputRowBottomBound(RemoteInputEntryAdapter entry, private void setRemoteInputActiveRow(RemoteInputEntryAdapter entry, boolean remoteInputActive) { ExpandableNotificationRow row = entry.getRow(); float top = row.getTranslationY(); int height = row.getActualHeight(); float bottom = top + height + row.getRemoteInputActionsContainerExpandedOffset(); mView.sendRemoteInputRowBottomBound(remoteInputActive ? bottom : null); mView.requestScrollToRemoteInput(remoteInputActive ? row : null); } }; } Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +65 −0 Original line number Diff line number Diff line Loading @@ -2056,6 +2056,69 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { assertThat(mStackScroller.getOwnScrollY()).isEqualTo(100 - childHeight); } @Test @EnableSceneContainer public void testRequestScrollToRemoteInput_singleRow() { final Consumer<Float> scroller = mock(FloatConsumer.class); mStackScroller.setRemoteInputRowBottomBoundConsumer(scroller); // GIVEN a non-grouped notification row with specific dimensions and offsets ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); when(row.getTranslationY()).thenReturn(100f); when(row.getActualHeight()).thenReturn(200); when(row.getRemoteInputActionsContainerExpandedOffset()).thenReturn(50f); mTestableResources.addOverride( com.android.internal.R.dimen.notification_content_margin, 10); // WHEN requestScrollToRemoteInput is called mStackScroller.requestScrollToRemoteInput(row); // THEN the correct bottom bound is sent // expected = translationY + height + remoteInputOffset + contentMargin // expected = 100 + 200 + 50 + 10 = 360 verify(scroller).accept(360f); // WHEN requestScrollToRemoteInput is called with null mStackScroller.requestScrollToRemoteInput(null); // THEN null is sent verify(scroller).accept(null); } @Test @EnableSceneContainer public void testRequestScrollToRemoteInput_childInGroup() { final Consumer<Float> scroller = mock(FloatConsumer.class); mStackScroller.setRemoteInputRowBottomBoundConsumer(scroller); // GIVEN a grouped notification row ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class); ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); when(row.getEntryAdapter()).thenReturn(mock(EntryAdapter.class)); when(mGroupMembershipManger.isChildInGroup(row.getEntryAdapter())).thenReturn(true); when(row.getTranslationY()).thenReturn(100f); when(row.getActualHeight()).thenReturn(200); when(row.getRemoteInputActionsContainerExpandedOffset()).thenReturn(50f); when(parent.getTranslationY()).thenReturn(50f); when(row.getNotificationParent()).thenReturn(parent); mTestableResources.addOverride( com.android.internal.R.dimen.notification_content_margin, 10); // WHEN requestScrollToRemoteInput is called for the grouped row mStackScroller.requestScrollToRemoteInput(row); // THEN the correct bottom bound is sent, including the parent's translation // expected = parentTranslationY + translationY + height + remoteInputOffset + contentMargin // expected = 50 + 100 + 200 + 50 + 10 = 410 verify(scroller).accept(410f); // WHEN requestScrollToRemoteInput is called with null mStackScroller.requestScrollToRemoteInput(null); // THEN null is sent verify(scroller).accept(null); } private MotionEvent captureTouchSentToSceneFramework() { ArgumentCaptor<MotionEvent> captor = ArgumentCaptor.forClass(MotionEvent.class); verify(mStackScrollLayoutController).sendTouchToSceneFramework(captor.capture()); Loading Loading @@ -2150,6 +2213,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { private abstract static class BooleanConsumer implements Consumer<Boolean> { } private abstract static class FloatConsumer implements Consumer<Float> { } private ShadeScrimShape createScrimShape(int left, int top, int right, int bottom) { ShadeScrimBounds bounds = new ShadeScrimBounds(left, top, right, bottom); return new ShadeScrimShape(bounds, 0, 0); Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +31 −5 Original line number Diff line number Diff line Loading @@ -766,15 +766,41 @@ public class NotificationStackScrollLayout mSectionsManager.reinflateViews(); } void sendRemoteInputRowBottomBound(Float bottom) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; if (bottom != null) { bottom += getResources().getDimensionPixelSize( com.android.internal.R.dimen.notification_content_margin); /** * Requests to scroll to the RemoteInput view of the given row. * @param row currently holding the active RemoteInput, or null if the RemoteInput is inactive. */ void requestScrollToRemoteInput(@Nullable ExpandableNotificationRow row) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { return; } // When SceneContainer is enabled, the NSSL doesn't handle its own scrolling. // Instead, it calculates the required scroll amount and emits it as a "synthetic scroll" // event. The Compose-based UI (`NotificationScrollingStack.kt`) consumes this event // and performs the actual scroll, synchronizing it with the IME animation. Float bottom = (row != null) ? getRemoteInputViewBottom(row) : null; mScrollViewFields.sendRemoteInputRowBottomBound(bottom); } /** * Calculates the Y position of the bottom of the remoteInput view, relative to the NSSL. * * @param row with an active remoteInput * @return The bottom Y position of the view relative to this layout. */ private float getRemoteInputViewBottom(ExpandableNotificationRow row) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f; // Calculate the base top position float topPosition = row.getTranslationY() // For nested notifications, add the parent's translation. + (isChildInGroup(row) ? row.getNotificationParent().getTranslationY() : 0f); int height = row.getActualHeight(); float remoteInputOffset = row.getRemoteInputActionsContainerExpandedOffset(); float contentMargin = getResources().getDimensionPixelSize( com.android.internal.R.dimen.notification_content_margin); return topPosition + height + remoteInputOffset + contentMargin; } void updateBgColor() { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +6 −7 Original line number Diff line number Diff line Loading @@ -1670,28 +1670,27 @@ public class NotificationStackScrollLayoutController implements Dumpable { public void setRemoteInputActive(RemoteInputEntryAdapter entry, boolean remoteInputActive) { if (SceneContainerFlag.isEnabled()) { sendRemoteInputRowBottomBound(entry, remoteInputActive); setRemoteInputActiveRow(entry, remoteInputActive); } entry.setRemoteInputActive(mHeadsUpManager, remoteInputActive); entry.notifyHeightChanged(true /* needsAnimation */); } public void lockScrollTo(ExpandableNotificationRow row) { if (!SceneContainerFlag.isEnabled()) { mView.lockScrollTo(row); } } public void requestDisallowLongPressAndDismiss() { mView.requestDisallowLongPress(); mView.requestDisallowDismiss(); } private void sendRemoteInputRowBottomBound(RemoteInputEntryAdapter entry, private void setRemoteInputActiveRow(RemoteInputEntryAdapter entry, boolean remoteInputActive) { ExpandableNotificationRow row = entry.getRow(); float top = row.getTranslationY(); int height = row.getActualHeight(); float bottom = top + height + row.getRemoteInputActionsContainerExpandedOffset(); mView.sendRemoteInputRowBottomBound(remoteInputActive ? bottom : null); mView.requestScrollToRemoteInput(remoteInputActive ? row : null); } }; } Loading