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

Commit 3b35daa8 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I465b207a,I42711886,I79e72ffd,I0d104471 into main

* changes:
  Channel SysUI state updates through SysUIStateDispatcher
  Extract StateChange and optimize it
  Cleanup SysUIState callback interface
  Propagate Notification and QS SysUI flags per display
parents b287224d c7d2e77a
Loading
Loading
Loading
Loading
+113 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.common.domain.interactor

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.model.StateChange
import com.android.systemui.model.fakeSysUIStatePerDisplayRepository
import com.android.systemui.model.sysUiStateFactory
import com.android.systemui.model.sysuiStateInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import org.junit.Before
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class SysUIStatePerDisplayInteractorTest : SysuiTestCase() {

    private val kosmos = testKosmos()

    val stateRepository = kosmos.fakeSysUIStatePerDisplayRepository
    val state0 = kosmos.sysUiStateFactory.create(0)
    val state1 = kosmos.sysUiStateFactory.create(1)
    val state2 = kosmos.sysUiStateFactory.create(2)

    val underTest = kosmos.sysuiStateInteractor

    @Before
    fun setup() {
        stateRepository.apply {
            add(0, state0)
            add(1, state1)
            add(2, state2)
        }
    }

    @Test
    fun setFlagsExclusivelyToDisplay_setsFlagsOnTargetStateAndClearsTheOthers() {
        val targetDisplayId = 0
        val stateChange = StateChange().setFlag(1L, true)

        underTest.setFlagsExclusivelyToDisplay(targetDisplayId, stateChange)

        assertThat(state0.isFlagEnabled(1)).isTrue()
        assertThat(state1.isFlagEnabled(1)).isFalse()
        assertThat(state2.isFlagEnabled(1)).isFalse()

        underTest.setFlagsExclusivelyToDisplay(1, stateChange)

        assertThat(state0.isFlagEnabled(1)).isFalse()
        assertThat(state1.isFlagEnabled(1)).isTrue()
        assertThat(state2.isFlagEnabled(1)).isFalse()

        underTest.setFlagsExclusivelyToDisplay(2, stateChange)

        assertThat(state0.isFlagEnabled(1)).isFalse()
        assertThat(state1.isFlagEnabled(1)).isFalse()
        assertThat(state2.isFlagEnabled(1)).isTrue()

        underTest.setFlagsExclusivelyToDisplay(3, stateChange)

        assertThat(state0.isFlagEnabled(1)).isFalse()
        assertThat(state1.isFlagEnabled(1)).isFalse()
        assertThat(state2.isFlagEnabled(1)).isFalse()
    }

    @Test
    fun setFlagsExclusivelyToDisplay_multipleFlags_setsFlagsOnTargetStateAndClearsTheOthers() {
        val stateChange = StateChange().setFlag(1L, true).setFlag(2L, true)

        underTest.setFlagsExclusivelyToDisplay(1, stateChange)

        assertThat(state0.isFlagEnabled(1)).isFalse()
        assertThat(state0.isFlagEnabled(2)).isFalse()
        assertThat(state1.isFlagEnabled(1)).isTrue()
        assertThat(state1.isFlagEnabled(2)).isTrue()
        assertThat(state2.isFlagEnabled(1)).isFalse()
        assertThat(state2.isFlagEnabled(1)).isFalse()
    }

    @Test
    fun setFlagsExclusivelyToDisplay_clearsFlags() {
        state0.setFlag(1, true).setFlag(2, true).commitUpdate()
        state1.setFlag(1, true).setFlag(2, true).commitUpdate()
        state2.setFlag(1, true).setFlag(2, true).commitUpdate()

        val stateChange = StateChange().setFlag(1L, false)

        underTest.setFlagsExclusivelyToDisplay(1, stateChange)

        // Sets it as false in display 1, but also the others.
        assertThat(state0.isFlagEnabled(1)).isFalse()
        assertThat(state1.isFlagEnabled(1)).isFalse()
        assertThat(state2.isFlagEnabled(1)).isFalse()
    }
}
+87 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.model

import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class SysUIStateDispatcherTest : SysuiTestCase() {

    private val kosmos = testKosmos()

    private val stateFactory = kosmos.sysUiStateFactory
    private val state0 = stateFactory.create(Display.DEFAULT_DISPLAY)
    private val state1 = stateFactory.create(DISPLAY_1)
    private val state2 = stateFactory.create(DISPLAY_2)
    private val underTest = kosmos.sysUIStateDispatcher

    private val flagsChanges = mutableMapOf<Int, Long>() // display id -> flag value
    private val callback =
        SysUiState.SysUiStateCallback { sysUiFlags, displayId ->
            flagsChanges[displayId] = sysUiFlags
        }

    @Test
    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
    fun registerUnregisterListener_notifiedOfChanges_receivedForAllDisplayIdsWithOneCallback() {
        underTest.registerListener(callback)

        state1.setFlag(FLAG_1, true).commitUpdate()
        state2.setFlag(FLAG_2, true).commitUpdate()

        assertThat(flagsChanges).containsExactly(DISPLAY_1, FLAG_1, DISPLAY_2, FLAG_2)

        underTest.unregisterListener(callback)

        state1.setFlag(0, true).commitUpdate()

        // Didn't change
        assertThat(flagsChanges).containsExactly(DISPLAY_1, FLAG_1, DISPLAY_2, FLAG_2)
    }

    @Test
    @DisableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
    fun registerUnregisterListener_notifiedOfChangesForNonDefaultDisplay_NotPropagated() {
        underTest.registerListener(callback)

        state1.setFlag(FLAG_1, true).commitUpdate()

        assertThat(flagsChanges).isEmpty()

        state0.setFlag(FLAG_1, true).commitUpdate()

        assertThat(flagsChanges).containsExactly(Display.DEFAULT_DISPLAY, FLAG_1)
    }

    private companion object {
        const val DISPLAY_1 = 1
        const val DISPLAY_2 = 2
        const val FLAG_1 = 10L
        const val FLAG_2 = 20L
    }
}
+17 −21
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import android.view.Display;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

@@ -53,9 +55,11 @@ public class SysUiStateTest extends SysuiTestCase {
    private SysUiState mFlagsContainer;
    private SceneContainerPlugin mSceneContainerPlugin;
    private DumpManager mDumpManager;
    private SysUIStateDispatcher mSysUIStateDispatcher;

    private SysUiState createInstance(int displayId) {
        var sysuiState = new SysUiStateImpl(displayId, mSceneContainerPlugin, mDumpManager);
        var sysuiState = new SysUiStateImpl(displayId, mSceneContainerPlugin, mDumpManager,
                mSysUIStateDispatcher);
        sysuiState.addCallback(mCallback);
        return sysuiState;
    }
@@ -67,6 +71,7 @@ public class SysUiStateTest extends SysuiTestCase {
        mSceneContainerPlugin = mKosmos.getSceneContainerPlugin();
        mCallback = mock(SysUiState.SysUiStateCallback.class);
        mDumpManager = mock(DumpManager.class);
        mSysUIStateDispatcher = mKosmos.getSysUIStateDispatcher();
        mFlagsContainer = createInstance(DEFAULT_DISPLAY);
    }

@@ -74,7 +79,7 @@ public class SysUiStateTest extends SysuiTestCase {
    public void addSingle_setFlag() {
        setFlags(FLAG_1);

        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
    }

    @Test
@@ -82,8 +87,8 @@ public class SysUiStateTest extends SysuiTestCase {
        setFlags(FLAG_1);
        setFlags(FLAG_2);

        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1 | FLAG_2);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1 | FLAG_2, DEFAULT_DISPLAY);
    }

    @Test
@@ -92,9 +97,9 @@ public class SysUiStateTest extends SysuiTestCase {
        setFlags(FLAG_2);
        mFlagsContainer.setFlag(FLAG_1, false).commitUpdate(DISPLAY_ID);

        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1 | FLAG_2);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_2);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1 | FLAG_2, DEFAULT_DISPLAY);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_2, DEFAULT_DISPLAY);
    }

    @Test
@@ -102,7 +107,7 @@ public class SysUiStateTest extends SysuiTestCase {
        setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4);

        int expected = FLAG_1 | FLAG_2 | FLAG_3 | FLAG_4;
        verify(mCallback, times(1)).onSystemUiStateChanged(expected);
        verify(mCallback, times(1)).onSystemUiStateChanged(expected, DEFAULT_DISPLAY);
    }

    @Test
@@ -111,9 +116,9 @@ public class SysUiStateTest extends SysuiTestCase {
        mFlagsContainer.setFlag(FLAG_2, false).commitUpdate(DISPLAY_ID);

        int expected1 = FLAG_1 | FLAG_2 | FLAG_3 | FLAG_4;
        verify(mCallback, times(1)).onSystemUiStateChanged(expected1);
        verify(mCallback, times(1)).onSystemUiStateChanged(expected1, DEFAULT_DISPLAY);
        int expected2 = FLAG_1 | FLAG_3 | FLAG_4;
        verify(mCallback, times(1)).onSystemUiStateChanged(expected2);
        verify(mCallback, times(1)).onSystemUiStateChanged(expected2, DEFAULT_DISPLAY);
    }

    @Test
@@ -122,25 +127,16 @@ public class SysUiStateTest extends SysuiTestCase {
        setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4);

        int expected = FLAG_1 | FLAG_2 | FLAG_3 | FLAG_4;
        verify(mCallback, times(0)).onSystemUiStateChanged(expected);
        verify(mCallback, times(0)).onSystemUiStateChanged(expected, DEFAULT_DISPLAY);
    }

    @Test
    public void setFlag_receivedForDefaultDisplay() {
        setFlags(FLAG_1);

        verify(mCallback, times(1)).onSystemUiStateChangedForDisplay(FLAG_1, DEFAULT_DISPLAY);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
    }

    @Test
    public void setFlag_externalDisplayInstance_defaultDisplayCallbackNotPropagated() {
        var instance = createInstance(/* displayId = */ 2);
        reset(mCallback);
        setFlags(instance, FLAG_1);

        verify(mCallback, times(1)).onSystemUiStateChangedForDisplay(FLAG_1, /* displayId= */ 2);
        verify(mCallback, never()).onSystemUiStateChanged(FLAG_1);
    }

    @Test
    public void init_registersWithDumpManager() {
+9 −0
Original line number Diff line number Diff line
@@ -17,7 +17,9 @@
package com.android.systemui.shade;

import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Display.TYPE_INTERNAL;

import static com.android.systemui.display.data.repository.FakeDisplayRepositoryKt.display;
import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;

import static com.google.common.truth.Truth.assertThat;
@@ -47,6 +49,7 @@ import android.os.Looper;
import android.os.PowerManager;
import android.os.UserManager;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -70,6 +73,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.common.domain.interactor.SysUIStateDisplaysInteractor;
import com.android.systemui.common.ui.view.TouchHandlingView;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
@@ -291,6 +295,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
    @Mock private NaturalScrollingSettingObserver mNaturalScrollingSettingObserver;
    @Mock private LargeScreenHeaderHelper mLargeScreenHeaderHelper;
    @Mock private StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector;
    @Mock protected SysUIStateDisplaysInteractor mSysUIStateDisplaysInteractor;
    protected final int mMaxUdfpsBurnInOffsetY = 5;
    protected FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
    protected KeyguardClockInteractor mKeyguardClockInteractor;
@@ -435,6 +440,9 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
            return null;
        }).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class));

        var displayMock = display(TYPE_INTERNAL, /* flags= */ 0, /* id= */Display.DEFAULT_DISPLAY,
                /* state= */ null);
        when(mView.getDisplay()).thenReturn(displayMock);
        // Any edge transition
        when(mKeyguardTransitionInteractor.transition(any()))
                .thenReturn(emptyFlow());
@@ -565,6 +573,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
                mShadeRepository,
                mSysUIUnfoldComponent,
                mSysUiState,
                mSysUIStateDisplaysInteractor,
                mKeyguardUnlockAnimationController,
                mKeyguardIndicationController,
                mNotificationListContainer,
+31 −0
Original line number Diff line number Diff line
@@ -16,10 +16,16 @@

package com.android.systemui.shade;

import static android.view.Display.TYPE_INTERNAL;

import static com.android.systemui.display.data.repository.FakeDisplayRepositoryKt.display;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -28,6 +34,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.os.Build;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.HapticFeedbackConstants;
@@ -38,6 +45,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.systemui.DejankUtils;
import com.android.systemui.Flags;
import com.android.systemui.flags.DisableSceneContainer;

import com.google.android.msdl.data.model.MSDLToken;
@@ -182,4 +190,27 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo

        assertThat(mMSDLPlayer.getLatestTokenPlayed()).isEqualTo(MSDLToken.FAILURE);
    }

    @Test
    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
    public void updateSystemUiStateFlags_updatesSysuiStateInteractor() {
        var DISPLAY_ID = 10;
        var displayMock = display(TYPE_INTERNAL, /* flags= */ 0, /* id= */DISPLAY_ID,
                /* state= */ null);
        when(mView.getDisplay()).thenReturn(displayMock);

        mNotificationPanelViewController.updateSystemUiStateFlags();

        verify(mSysUIStateDisplaysInteractor).setFlagsExclusivelyToDisplay(eq(DISPLAY_ID), any());
    }

    @Test
    @DisableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
    public void updateSystemUiStateFlags_flagOff_doesNotUpdateSysuiStateInteractor() {
        mNotificationPanelViewController.updateSystemUiStateFlags();

        verify(mSysUIStateDisplaysInteractor, never()).setFlagsExclusivelyToDisplay(anyInt(),
                any());
    }

}
Loading