Loading packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -1919,3 +1919,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "stabilize_heads_up_group" namespace: "systemui" description: "Stabilize heads up groups in VisualStabilityCoordinator" bug: "381864715" metadata { purpose: PURPOSE_BUGFIX } } packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java +222 −6 Original line number Diff line number Diff line Loading @@ -16,17 +16,21 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag; import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; import static org.junit.Assume.assumeFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; Loading @@ -48,6 +52,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.BrokenWithSceneContainer; import com.android.systemui.flags.DisableSceneContainer; import com.android.systemui.flags.EnableSceneContainer; import com.android.systemui.flags.SceneContainerFlagParameterizationKt; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionState; Loading Loading @@ -86,9 +91,11 @@ import org.mockito.MockitoAnnotations; import org.mockito.verification.VerificationMode; import java.util.List; import java.util.Set; import kotlinx.coroutines.flow.MutableStateFlow; import kotlinx.coroutines.test.TestScope; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; Loading @@ -99,7 +106,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { return parameterizeSceneContainerFlag(); return SceneContainerFlagParameterizationKt .andSceneContainer(allCombinationsOf(Flags.FLAG_STABILIZE_HEADS_UP_GROUP)); } private VisualStabilityCoordinator mCoordinator; Loading @@ -122,7 +130,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); private FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock); private FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock); private FakeExecutor mFakeMainExecutor = new FakeExecutor(mFakeSystemClock); private final TestScope mTestScope = mKosmos.getTestScope(); private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope()); Loading @@ -147,7 +156,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl( new ShadeAnimationRepository(), mShadeRepository); mCoordinator = new VisualStabilityCoordinator( mFakeExecutor, mFakeBackgroundExecutor, mFakeMainExecutor, mDumpManager, mHeadsUpManager, mShadeAnimationInteractor, Loading Loading @@ -417,8 +427,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); // WHEN the timeout for the temporarily allow section reordering runnable is finsihed mFakeExecutor.advanceClockToNext(); mFakeExecutor.runNextReady(); mFakeBackgroundExecutor.advanceClockToNext(); mFakeBackgroundExecutor.runNextReady(); // THEN section changes aren't allowed anymore assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); Loading Loading @@ -701,6 +711,212 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry)); } @Test public void everyChangeAllowed_onReorderingEnabled_legacy() { assumeFalse(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // THEN assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isTrue(); assertThat(mNotifStabilityManager.isGroupChangeAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isGroupPruneAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isSectionChangeAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(any())).isTrue(); } @Test public void everyChangeAllowed_noActiveHeadsUpGroup_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - empty heads-up-group keys mCoordinator.setHeadsUpGroupKeys(Set.of()); // THEN assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isTrue(); assertThat(mNotifStabilityManager.isGroupChangeAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isGroupPruneAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isSectionChangeAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(any())).isTrue(); } @Test public void everyChangeDisallowed_activeHeadsUpGroup_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. mCoordinator.setHeadsUpGroupKeys(Set.of("heads_up_group_key")); // THEN assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isFalse(); } @Test public void nonHeadsUpGroup_changesAllowed_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. String headsUpGroupKey = "heads_up_group_key"; mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey)); when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true); // GIVEN - HUN Group Summary final NotificationEntry nonHeadsUpGroupSummary = mock(NotificationEntry.class); when(nonHeadsUpGroupSummary.getKey()).thenReturn("non_heads_up_group_key"); when(nonHeadsUpGroupSummary.isSummaryWithChildren()).thenReturn(true); final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class); when(nonHeadsUpGroupEntry.getSummary()).thenReturn(nonHeadsUpGroupSummary); when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(nonHeadsUpGroupSummary); // THEN assertThat(mNotifStabilityManager.isGroupPruneAllowed(nonHeadsUpGroupEntry)).isTrue(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry)).isTrue(); } @Test public void headsUpGroup_changesDisallowed_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. final String headsUpGroupKey = "heads_up_group_key"; mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey)); when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true); // GIVEN - HUN Group final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class); when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false); when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey); when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true); final GroupEntry headsUpGroupEntry = mock(GroupEntry.class); when(headsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary); when(headsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary); when(headsUpGroupSummary.getParent()).thenReturn(headsUpGroupEntry); // GIVEN - HUN is in visible location when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true); // THEN assertThat(mNotifStabilityManager.isGroupPruneAllowed(headsUpGroupEntry)).isFalse(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(headsUpGroupEntry)).isFalse(); } @Test public void headsUpGroupSummaries_changesDisallowed_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. final String headsUpGroupKey = "heads_up_group_key"; mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey)); when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true); // GIVEN - HUN Group final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class); when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false); when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey); when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true); final GroupEntry headsUpGroupEntry = mock(GroupEntry.class); when(headsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary); when(headsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary); when(headsUpGroupSummary.getParent()).thenReturn(headsUpGroupEntry); // GIVEN - HUN is in visible location when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true); // THEN assertThat(mNotifStabilityManager.isGroupChangeAllowed(headsUpGroupSummary)).isFalse(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(headsUpGroupSummary)).isFalse(); assertThat(mNotifStabilityManager.isSectionChangeAllowed(headsUpGroupSummary)).isFalse(); } @Test public void notificationInNonHUNGroup_changesAllowed_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. String headsUpGroupKey = "heads_up_group_key"; mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey)); when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true); // GIVEN - non HUN parent Group Summary final NotificationEntry groupSummary = mock(NotificationEntry.class); when(groupSummary.getKey()).thenReturn("non_heads_up_group_key"); when(groupSummary.isSummaryWithChildren()).thenReturn(true); final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class); when(nonHeadsUpGroupEntry.getSummary()).thenReturn(groupSummary); when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(groupSummary); // GIVEN - child entry in a non heads-up group. final NotificationEntry childEntry = mock(NotificationEntry.class); when(childEntry.rowIsChildInGroup()).thenReturn(true); when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry); when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry); // THEN assertThat(mNotifStabilityManager.isGroupChangeAllowed(childEntry)).isTrue(); assertThat(mNotifStabilityManager.isSectionChangeAllowed(childEntry)).isTrue(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry)).isTrue(); } @Test public void notificationInHUNGroup_changesDisallowed_reorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. final String headsUpGroupKey = "heads_up_group_key"; mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey)); when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true); // GIVEN - HUN Group Summary final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class); when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false); when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey); when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true); final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class); when(nonHeadsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary); when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary); // GIVEN - child entry in a non heads-up group. final NotificationEntry childEntry = mock(NotificationEntry.class); when(childEntry.rowIsChildInGroup()).thenReturn(true); when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry); // GIVEN - HUN is in visible location when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true); // THEN assertThat(mNotifStabilityManager.isGroupChangeAllowed(childEntry)).isFalse(); assertThat(mNotifStabilityManager.isSectionChangeAllowed(childEntry)).isFalse(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(childEntry)).isFalse(); } private void verifyStabilityManagerWasInvalidated(VerificationMode mode) { verify(mInvalidateListener, mode).onPluggableInvalidated(eq(mNotifStabilityManager), any()); } Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StabilizeHeadsUpGroup.kt 0 → 100644 +61 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.notification.collection.coordinator import com.android.systemui.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils /** Helper for reading or using the every change not allowed in heads up Group mode flag state. */ @Suppress("NOTHING_TO_INLINE") object StabilizeHeadsUpGroup { /** The aconfig flag name */ const val FLAG_NAME: String = Flags.FLAG_STABILIZE_HEADS_UP_GROUP /** A token used for dependency declaration */ val token: FlagToken get() = FlagToken(FLAG_NAME, isEnabled) /** Is the refactor enabled */ @JvmStatic inline val isEnabled get() = Flags.stabilizeHeadsUpGroup() /** * Called to ensure code is only run when the flag is enabled. This protects users from the * unintended behaviors caused by accidentally running new logic, while also crashing on an eng * build to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun isUnexpectedlyInLegacyMode() = RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) /** * Called to ensure code is only run when the flag is enabled. This will throw an exception if * the flag is not enabled to ensure that the refactor author catches issues in testing. * Caution!! Using this check incorrectly will cause crashes in nextfood builds! */ @JvmStatic inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME) /** * Called to ensure code is only run when the flag is disabled. This will throw an exception if * the flag is enabled to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) } packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +192 −14 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorLogger.kt +5 −2 Original line number Diff line number Diff line Loading @@ -32,7 +32,8 @@ constructor(@VisualStabilityLog private val buffer: LogBuffer) { wasReorderingAllowed: Boolean, isReorderingAllowed: Boolean, field: String, value: Boolean value: Boolean, async: Boolean, ) { buffer.log( TAG, Loading @@ -44,13 +45,15 @@ constructor(@VisualStabilityLog private val buffer: LogBuffer) { bool4 = isReorderingAllowed str1 = field str2 = value.toString() str3 = async.toString() }, { "stability allowances changed:" + " pipelineRunAllowed $bool1->$bool2" + " reorderingAllowed $bool3->$bool4" + " when setting $str1=$str2" } " async=$str3" }, ) } } Loading
packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -1919,3 +1919,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "stabilize_heads_up_group" namespace: "systemui" description: "Stabilize heads up groups in VisualStabilityCoordinator" bug: "381864715" metadata { purpose: PURPOSE_BUGFIX } }
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java +222 −6 Original line number Diff line number Diff line Loading @@ -16,17 +16,21 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag; import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; import static org.junit.Assume.assumeFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; Loading @@ -48,6 +52,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.BrokenWithSceneContainer; import com.android.systemui.flags.DisableSceneContainer; import com.android.systemui.flags.EnableSceneContainer; import com.android.systemui.flags.SceneContainerFlagParameterizationKt; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionState; Loading Loading @@ -86,9 +91,11 @@ import org.mockito.MockitoAnnotations; import org.mockito.verification.VerificationMode; import java.util.List; import java.util.Set; import kotlinx.coroutines.flow.MutableStateFlow; import kotlinx.coroutines.test.TestScope; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; Loading @@ -99,7 +106,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { return parameterizeSceneContainerFlag(); return SceneContainerFlagParameterizationKt .andSceneContainer(allCombinationsOf(Flags.FLAG_STABILIZE_HEADS_UP_GROUP)); } private VisualStabilityCoordinator mCoordinator; Loading @@ -122,7 +130,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); private FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock); private FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock); private FakeExecutor mFakeMainExecutor = new FakeExecutor(mFakeSystemClock); private final TestScope mTestScope = mKosmos.getTestScope(); private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope()); Loading @@ -147,7 +156,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl( new ShadeAnimationRepository(), mShadeRepository); mCoordinator = new VisualStabilityCoordinator( mFakeExecutor, mFakeBackgroundExecutor, mFakeMainExecutor, mDumpManager, mHeadsUpManager, mShadeAnimationInteractor, Loading Loading @@ -417,8 +427,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); // WHEN the timeout for the temporarily allow section reordering runnable is finsihed mFakeExecutor.advanceClockToNext(); mFakeExecutor.runNextReady(); mFakeBackgroundExecutor.advanceClockToNext(); mFakeBackgroundExecutor.runNextReady(); // THEN section changes aren't allowed anymore assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); Loading Loading @@ -701,6 +711,212 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry)); } @Test public void everyChangeAllowed_onReorderingEnabled_legacy() { assumeFalse(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // THEN assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isTrue(); assertThat(mNotifStabilityManager.isGroupChangeAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isGroupPruneAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isSectionChangeAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(any())).isTrue(); } @Test public void everyChangeAllowed_noActiveHeadsUpGroup_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - empty heads-up-group keys mCoordinator.setHeadsUpGroupKeys(Set.of()); // THEN assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isTrue(); assertThat(mNotifStabilityManager.isGroupChangeAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isGroupPruneAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isSectionChangeAllowed(any())).isTrue(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(any())).isTrue(); } @Test public void everyChangeDisallowed_activeHeadsUpGroup_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. mCoordinator.setHeadsUpGroupKeys(Set.of("heads_up_group_key")); // THEN assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isFalse(); } @Test public void nonHeadsUpGroup_changesAllowed_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. String headsUpGroupKey = "heads_up_group_key"; mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey)); when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true); // GIVEN - HUN Group Summary final NotificationEntry nonHeadsUpGroupSummary = mock(NotificationEntry.class); when(nonHeadsUpGroupSummary.getKey()).thenReturn("non_heads_up_group_key"); when(nonHeadsUpGroupSummary.isSummaryWithChildren()).thenReturn(true); final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class); when(nonHeadsUpGroupEntry.getSummary()).thenReturn(nonHeadsUpGroupSummary); when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(nonHeadsUpGroupSummary); // THEN assertThat(mNotifStabilityManager.isGroupPruneAllowed(nonHeadsUpGroupEntry)).isTrue(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry)).isTrue(); } @Test public void headsUpGroup_changesDisallowed_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. final String headsUpGroupKey = "heads_up_group_key"; mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey)); when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true); // GIVEN - HUN Group final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class); when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false); when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey); when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true); final GroupEntry headsUpGroupEntry = mock(GroupEntry.class); when(headsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary); when(headsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary); when(headsUpGroupSummary.getParent()).thenReturn(headsUpGroupEntry); // GIVEN - HUN is in visible location when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true); // THEN assertThat(mNotifStabilityManager.isGroupPruneAllowed(headsUpGroupEntry)).isFalse(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(headsUpGroupEntry)).isFalse(); } @Test public void headsUpGroupSummaries_changesDisallowed_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. final String headsUpGroupKey = "heads_up_group_key"; mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey)); when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true); // GIVEN - HUN Group final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class); when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false); when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey); when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true); final GroupEntry headsUpGroupEntry = mock(GroupEntry.class); when(headsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary); when(headsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary); when(headsUpGroupSummary.getParent()).thenReturn(headsUpGroupEntry); // GIVEN - HUN is in visible location when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true); // THEN assertThat(mNotifStabilityManager.isGroupChangeAllowed(headsUpGroupSummary)).isFalse(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(headsUpGroupSummary)).isFalse(); assertThat(mNotifStabilityManager.isSectionChangeAllowed(headsUpGroupSummary)).isFalse(); } @Test public void notificationInNonHUNGroup_changesAllowed_onReorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. String headsUpGroupKey = "heads_up_group_key"; mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey)); when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true); // GIVEN - non HUN parent Group Summary final NotificationEntry groupSummary = mock(NotificationEntry.class); when(groupSummary.getKey()).thenReturn("non_heads_up_group_key"); when(groupSummary.isSummaryWithChildren()).thenReturn(true); final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class); when(nonHeadsUpGroupEntry.getSummary()).thenReturn(groupSummary); when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(groupSummary); // GIVEN - child entry in a non heads-up group. final NotificationEntry childEntry = mock(NotificationEntry.class); when(childEntry.rowIsChildInGroup()).thenReturn(true); when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry); when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry); // THEN assertThat(mNotifStabilityManager.isGroupChangeAllowed(childEntry)).isTrue(); assertThat(mNotifStabilityManager.isSectionChangeAllowed(childEntry)).isTrue(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry)).isTrue(); } @Test public void notificationInHUNGroup_changesDisallowed_reorderingEnabled() { assumeTrue(StabilizeHeadsUpGroup.isEnabled()); // GIVEN - reordering is allowed. setPulsing(false); setPanelExpanded(false); // GIVEN - there is a group heads-up. final String headsUpGroupKey = "heads_up_group_key"; mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey)); when(mHeadsUpManager.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true); // GIVEN - HUN Group Summary final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class); when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false); when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey); when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true); final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class); when(nonHeadsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary); when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary); // GIVEN - child entry in a non heads-up group. final NotificationEntry childEntry = mock(NotificationEntry.class); when(childEntry.rowIsChildInGroup()).thenReturn(true); when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry); // GIVEN - HUN is in visible location when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true); // THEN assertThat(mNotifStabilityManager.isGroupChangeAllowed(childEntry)).isFalse(); assertThat(mNotifStabilityManager.isSectionChangeAllowed(childEntry)).isFalse(); assertThat(mNotifStabilityManager.isEntryReorderingAllowed(childEntry)).isFalse(); } private void verifyStabilityManagerWasInvalidated(VerificationMode mode) { verify(mInvalidateListener, mode).onPluggableInvalidated(eq(mNotifStabilityManager), any()); } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StabilizeHeadsUpGroup.kt 0 → 100644 +61 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.notification.collection.coordinator import com.android.systemui.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils /** Helper for reading or using the every change not allowed in heads up Group mode flag state. */ @Suppress("NOTHING_TO_INLINE") object StabilizeHeadsUpGroup { /** The aconfig flag name */ const val FLAG_NAME: String = Flags.FLAG_STABILIZE_HEADS_UP_GROUP /** A token used for dependency declaration */ val token: FlagToken get() = FlagToken(FLAG_NAME, isEnabled) /** Is the refactor enabled */ @JvmStatic inline val isEnabled get() = Flags.stabilizeHeadsUpGroup() /** * Called to ensure code is only run when the flag is enabled. This protects users from the * unintended behaviors caused by accidentally running new logic, while also crashing on an eng * build to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun isUnexpectedlyInLegacyMode() = RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) /** * Called to ensure code is only run when the flag is enabled. This will throw an exception if * the flag is not enabled to ensure that the refactor author catches issues in testing. * Caution!! Using this check incorrectly will cause crashes in nextfood builds! */ @JvmStatic inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME) /** * Called to ensure code is only run when the flag is disabled. This will throw an exception if * the flag is enabled to ensure that the refactor author catches issues in testing. */ @JvmStatic inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) }
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +192 −14 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorLogger.kt +5 −2 Original line number Diff line number Diff line Loading @@ -32,7 +32,8 @@ constructor(@VisualStabilityLog private val buffer: LogBuffer) { wasReorderingAllowed: Boolean, isReorderingAllowed: Boolean, field: String, value: Boolean value: Boolean, async: Boolean, ) { buffer.log( TAG, Loading @@ -44,13 +45,15 @@ constructor(@VisualStabilityLog private val buffer: LogBuffer) { bool4 = isReorderingAllowed str1 = field str2 = value.toString() str3 = async.toString() }, { "stability allowances changed:" + " pipelineRunAllowed $bool1->$bool2" + " reorderingAllowed $bool3->$bool4" + " when setting $str1=$str2" } " async=$str3" }, ) } }