Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf0be2ea authored by Ibrahim Yilmaz's avatar Ibrahim Yilmaz Committed by Android (Google) Code Review
Browse files

Merge "Disallow changes for heads up group and notifications in heads up group" into main

parents e85e9663 6353f63e
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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
    }
}
+222 −6
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;

@@ -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;
@@ -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());

@@ -147,7 +156,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
        mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl(
                new ShadeAnimationRepository(), mShadeRepository);
        mCoordinator = new VisualStabilityCoordinator(
                mFakeExecutor,
                mFakeBackgroundExecutor,
                mFakeMainExecutor,
                mDumpManager,
                mHeadsUpManager,
                mShadeAnimationInteractor,
@@ -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));
@@ -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());
    }
+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)
}
+192 −14

File changed.

Preview size limit exceeded, changes collapsed.

+5 −2
Original line number Diff line number Diff line
@@ -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,
@@ -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"
            },
        )
    }
}