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

Commit 65679fbb authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Fixes "wallpaper not visible on AOD" bug.

The reason the wallpaper wasn't visible was because the notification
shade window was being set to opaque which tells the window manager to
not render windows that are below it on the z-axis.

The reason it was being set to opaque was because ScrimController was
setting the alpha of one of the scrims to 1.0 when Flexiglass was on but
to 0.2 when Flexiglass was off.

The reason that was happening was because the alpha of the scrim depends
on the "panel expansion" amount (from 0.0 to 1.0) and this is a concept
that no longer exists in Flexiglass so the legacy code was treating this
as a 1.0 at all times.

The CL adds the panel expansion concept but clearly marks it as
@Deprecated and legacy and ties the amount to Flexiglass state. Finally,
it connects the logic in ShadeTransitionController to the old-new
concept when Flexiglass is on and disables the old way.

Fix: 323072803
Test: unit test added for PanelExpansionInteractor
Test: manually verified that the wallpaper is visible in AOD
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Change-Id: Iea0cb312aab442ac18fb39b17376fc2b22090d65
parent 9fcf4690
Loading
Loading
Loading
Loading
+191 −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.
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.scene.domain.interactor

import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.panelExpansionInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

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

    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository
    private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
    private val sceneInteractor = kosmos.sceneInteractor
    private val transitionState =
        MutableStateFlow<ObservableTransitionState>(
            ObservableTransitionState.Idle(SceneKey.Lockscreen)
        )
    private val fakeSceneDataSource = kosmos.fakeSceneDataSource
    private val fakeShadeRepository = kosmos.fakeShadeRepository

    private lateinit var underTest: PanelExpansionInteractor

    @Before
    fun setUp() {
        sceneInteractor.setTransitionState(transitionState)
    }

    @Test
    @EnableSceneContainer
    fun legacyPanelExpansion_whenIdle_whenLocked() =
        testScope.runTest {
            underTest = kosmos.panelExpansionInteractor
            setUnlocked(false)
            val panelExpansion by collectLastValue(underTest.legacyPanelExpansion)

            changeScene(SceneKey.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) }
            assertThat(panelExpansion).isEqualTo(1f)

            changeScene(SceneKey.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) }
            assertThat(panelExpansion).isEqualTo(1f)

            changeScene(SceneKey.Shade) { assertThat(panelExpansion).isEqualTo(1f) }
            assertThat(panelExpansion).isEqualTo(1f)

            changeScene(SceneKey.QuickSettings) { assertThat(panelExpansion).isEqualTo(1f) }
            assertThat(panelExpansion).isEqualTo(1f)

            changeScene(SceneKey.Communal) { assertThat(panelExpansion).isEqualTo(1f) }
            assertThat(panelExpansion).isEqualTo(1f)
        }

    @Test
    @EnableSceneContainer
    fun legacyPanelExpansion_whenIdle_whenUnlocked() =
        testScope.runTest {
            underTest = kosmos.panelExpansionInteractor
            setUnlocked(true)
            val panelExpansion by collectLastValue(underTest.legacyPanelExpansion)

            changeScene(SceneKey.Gone) { assertThat(panelExpansion).isEqualTo(0f) }
            assertThat(panelExpansion).isEqualTo(0f)

            changeScene(SceneKey.Shade) { progress ->
                assertThat(panelExpansion).isEqualTo(progress)
            }
            assertThat(panelExpansion).isEqualTo(1f)

            changeScene(SceneKey.QuickSettings) {
                // Shade's already expanded, so moving to QS should also be 1f.
                assertThat(panelExpansion).isEqualTo(1f)
            }
            assertThat(panelExpansion).isEqualTo(1f)

            changeScene(SceneKey.Communal) { assertThat(panelExpansion).isEqualTo(1f) }
            assertThat(panelExpansion).isEqualTo(1f)
        }

    @Test
    @DisableFlags(FLAG_SCENE_CONTAINER)
    fun legacyPanelExpansion_whenInLegacyMode() =
        testScope.runTest {
            underTest = kosmos.panelExpansionInteractor
            val leet = 0.1337f
            fakeShadeRepository.setLegacyShadeExpansion(leet)
            setUnlocked(false)
            val panelExpansion by collectLastValue(underTest.legacyPanelExpansion)

            changeScene(SceneKey.Lockscreen)
            assertThat(panelExpansion).isEqualTo(leet)

            changeScene(SceneKey.Bouncer)
            assertThat(panelExpansion).isEqualTo(leet)

            changeScene(SceneKey.Shade)
            assertThat(panelExpansion).isEqualTo(leet)

            changeScene(SceneKey.QuickSettings)
            assertThat(panelExpansion).isEqualTo(leet)

            changeScene(SceneKey.Communal)
            assertThat(panelExpansion).isEqualTo(leet)
        }

    private fun TestScope.setUnlocked(isUnlocked: Boolean) {
        val isDeviceUnlocked by collectLastValue(deviceUnlockedInteractor.isDeviceUnlocked)
        deviceEntryRepository.setUnlocked(isUnlocked)
        runCurrent()

        assertThat(isDeviceUnlocked).isEqualTo(isUnlocked)
    }

    private fun TestScope.changeScene(
        toScene: SceneKey,
        assertDuringProgress: ((progress: Float) -> Unit) = {},
    ) {
        val currentScene by collectLastValue(sceneInteractor.currentScene)
        val progressFlow = MutableStateFlow(0f)
        transitionState.value =
            ObservableTransitionState.Transition(
                fromScene = checkNotNull(currentScene),
                toScene = toScene,
                progress = progressFlow,
                isInitiatedByUserInput = true,
                isUserInputOngoing = flowOf(true),
            )
        runCurrent()
        assertDuringProgress(progressFlow.value)

        progressFlow.value = 0.2f
        runCurrent()
        assertDuringProgress(progressFlow.value)

        progressFlow.value = 0.6f
        runCurrent()
        assertDuringProgress(progressFlow.value)

        progressFlow.value = 1f
        runCurrent()
        assertDuringProgress(progressFlow.value)

        transitionState.value = ObservableTransitionState.Idle(toScene)
        fakeSceneDataSource.changeScene(toScene)
        runCurrent()
        assertDuringProgress(progressFlow.value)

        assertThat(currentScene).isEqualTo(toScene)
    }
}
+104 −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.
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.scene.domain.interactor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.data.repository.ShadeRepository
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map

@SysUISingleton
class PanelExpansionInteractor
@Inject
constructor(
    sceneInteractor: SceneInteractor,
    shadeRepository: ShadeRepository,
) {

    /**
     * The amount by which the "panel" has been expanded (`0` when fully collapsed, `1` when fully
     * expanded).
     *
     * This is a legacy concept from the time when the "panel" included the notification/QS shades
     * as well as the keyguard (lockscreen and bouncer). This value is meant only for
     * backwards-compatibility and should not be consumed by newer code.
     */
    @Deprecated("Use SceneInteractor.currentScene instead.")
    val legacyPanelExpansion: Flow<Float> =
        if (SceneContainerFlag.isEnabled) {
            sceneInteractor.transitionState.flatMapLatest { state ->
                when (state) {
                    is ObservableTransitionState.Idle ->
                        flowOf(
                            if (state.scene != SceneKey.Gone) {
                                // When resting on a non-Gone scene, the panel is fully expanded.
                                1f
                            } else {
                                // When resting on the Gone scene, the panel is considered fully
                                // collapsed.
                                0f
                            }
                        )
                    is ObservableTransitionState.Transition ->
                        when {
                            state.fromScene == SceneKey.Gone ->
                                if (state.toScene.isExpandable()) {
                                    // Moving from Gone to a scene that can animate-expand has a
                                    // panel
                                    // expansion
                                    // that tracks with the transition.
                                    state.progress
                                } else {
                                    // Moving from Gone to a scene that doesn't animate-expand
                                    // immediately makes
                                    // the panel fully expanded.
                                    flowOf(1f)
                                }
                            state.toScene == SceneKey.Gone ->
                                if (state.fromScene.isExpandable()) {
                                    // Moving to Gone from a scene that can animate-expand has a
                                    // panel
                                    // expansion
                                    // that tracks with the transition.
                                    state.progress.map { 1 - it }
                                } else {
                                    // Moving to Gone from a scene that doesn't animate-expand
                                    // immediately makes
                                    // the panel fully collapsed.
                                    flowOf(0f)
                                }
                            else -> flowOf(1f)
                        }
                }
            }
        } else {
            shadeRepository.legacyShadeExpansion
        }

    private fun SceneKey.isExpandable(): Boolean {
        return this == SceneKey.Shade || this == SceneKey.QuickSettings
    }
}
+33 −7
Original line number Diff line number Diff line
@@ -19,8 +19,11 @@ package com.android.systemui.shade.transition
import android.content.Context
import android.content.res.Configuration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.scene.domain.interactor.PanelExpansionInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.PanelState
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionStateManager
@@ -31,21 +34,26 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.SplitShadeStateController
import dagger.Lazy
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

/** Controls the shade expansion transition on non-lockscreen. */
@SysUISingleton
class ShadeTransitionController
@Inject
constructor(
    @Application private val applicationScope: CoroutineScope,
    configurationController: ConfigurationController,
    shadeExpansionStateManager: ShadeExpansionStateManager,
    dumpManager: DumpManager,
    private val context: Context,
    private val scrimShadeTransitionController: ScrimShadeTransitionController,
    private val statusBarStateController: SysuiStatusBarStateController,
    private val splitShadeStateController: SplitShadeStateController
    private val splitShadeStateController: SplitShadeStateController,
    private val panelExpansionInteractor: Lazy<PanelExpansionInteractor>,
) {

    lateinit var shadeViewController: ShadeViewController
@@ -63,11 +71,27 @@ constructor(
                override fun onConfigChanged(newConfig: Configuration?) {
                    updateResources()
                }
            })
            }
        )
        if (SceneContainerFlag.isEnabled) {
            applicationScope.launch {
                panelExpansionInteractor.get().legacyPanelExpansion.collect { panelExpansion ->
                    onPanelExpansionChanged(
                        ShadeExpansionChangeEvent(
                            fraction = panelExpansion,
                            expanded = panelExpansion > 0f,
                            tracking = true,
                            dragDownPxAmount = 0f,
                        )
                    )
                }
            }
        } else {
            val currentState =
                shadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged)
            onPanelExpansionChanged(currentState)
            shadeExpansionStateManager.addStateListener(this::onPanelStateChanged)
        }
        dumpManager.registerCriticalDumpable("ShadeTransitionController") { printWriter, _ ->
            dump(printWriter)
        }
@@ -98,7 +122,9 @@ constructor(
                qs.isInitialized: ${this::qs.isInitialized}
                npvc.isInitialized: ${this::shadeViewController.isInitialized}
                nssl.isInitialized: ${this::notificationStackScrollLayoutController.isInitialized}
            """.trimIndent())
            """
                .trimIndent()
        )
    }

    private fun isScreenUnlocked() =
+146 −5
Original line number Diff line number Diff line
@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.shade.transition

import android.platform.test.annotations.DisableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.PanelExpansionInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.FakeSceneDataSource
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.shade.STATE_OPENING
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.panelExpansionInteractor
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -29,32 +60,95 @@ class ShadeTransitionControllerTest : SysuiTestCase() {

    private val configurationController = FakeConfigurationController()
    private val shadeExpansionStateManager = ShadeExpansionStateManager()
    private val kosmos = testKosmos()
    private lateinit var testScope: TestScope
    private lateinit var applicationScope: CoroutineScope
    private lateinit var panelExpansionInteractor: PanelExpansionInteractor
    private lateinit var deviceEntryRepository: FakeDeviceEntryRepository
    private lateinit var deviceUnlockedInteractor: DeviceUnlockedInteractor
    private lateinit var sceneInteractor: SceneInteractor
    private lateinit var fakeSceneDataSource: FakeSceneDataSource

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)

        testScope = kosmos.testScope
        applicationScope = kosmos.applicationCoroutineScope
        panelExpansionInteractor = kosmos.panelExpansionInteractor
        deviceEntryRepository = kosmos.fakeDeviceEntryRepository
        deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
        sceneInteractor = kosmos.sceneInteractor
        fakeSceneDataSource = kosmos.fakeSceneDataSource

        controller =
            ShadeTransitionController(
                applicationScope,
                configurationController,
                shadeExpansionStateManager,
                dumpManager,
                context,
                scrimShadeTransitionController,
                statusBarStateController,
                    ResourcesSplitShadeStateController()
            )
                ResourcesSplitShadeStateController(),
            ) {
                panelExpansionInteractor
            }
    }

    @Test
    @DisableFlags(FLAG_SCENE_CONTAINER)
    fun onPanelStateChanged_forwardsToScrimTransitionController() {
        startPanelExpansion()
        startLegacyPanelExpansion()

        verify(scrimShadeTransitionController).onPanelStateChanged(STATE_OPENING)
        verify(scrimShadeTransitionController).onPanelExpansionChanged(DEFAULT_EXPANSION_EVENT)
    }

    private fun startPanelExpansion() {
    @Test
    @EnableSceneContainer
    fun sceneChanges_forwardsToScrimTransitionController() =
        testScope.runTest {
            var latestChangeEvent: ShadeExpansionChangeEvent? = null
            whenever(scrimShadeTransitionController.onPanelExpansionChanged(any())).thenAnswer {
                latestChangeEvent = it.arguments[0] as ShadeExpansionChangeEvent
                Unit
            }
            setUnlocked(true)
            val transitionState =
                MutableStateFlow<ObservableTransitionState>(
                    ObservableTransitionState.Idle(SceneKey.Gone)
                )
            sceneInteractor.setTransitionState(transitionState)

            changeScene(SceneKey.Gone, transitionState)
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            assertThat(currentScene).isEqualTo(SceneKey.Gone)

            assertThat(latestChangeEvent)
                .isEqualTo(
                    ShadeExpansionChangeEvent(
                        fraction = 0f,
                        expanded = false,
                        tracking = true,
                        dragDownPxAmount = 0f,
                    )
                )

            changeScene(SceneKey.Shade, transitionState) { progress ->
                assertThat(latestChangeEvent)
                    .isEqualTo(
                        ShadeExpansionChangeEvent(
                            fraction = progress,
                            expanded = progress > 0,
                            tracking = true,
                            dragDownPxAmount = 0f,
                        )
                    )
            }
        }

    private fun startLegacyPanelExpansion() {
        shadeExpansionStateManager.onPanelExpansionChanged(
            DEFAULT_EXPANSION_EVENT.fraction,
            DEFAULT_EXPANSION_EVENT.expanded,
@@ -63,6 +157,52 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
        )
    }

    private fun TestScope.setUnlocked(isUnlocked: Boolean) {
        val isDeviceUnlocked by collectLastValue(deviceUnlockedInteractor.isDeviceUnlocked)
        deviceEntryRepository.setUnlocked(isUnlocked)
        runCurrent()

        assertThat(isDeviceUnlocked).isEqualTo(isUnlocked)
    }

    private fun TestScope.changeScene(
        toScene: SceneKey,
        transitionState: MutableStateFlow<ObservableTransitionState>,
        assertDuringProgress: ((progress: Float) -> Unit) = {},
    ) {
        val currentScene by collectLastValue(sceneInteractor.currentScene)
        val progressFlow = MutableStateFlow(0f)
        transitionState.value =
            ObservableTransitionState.Transition(
                fromScene = checkNotNull(currentScene),
                toScene = toScene,
                progress = progressFlow,
                isInitiatedByUserInput = true,
                isUserInputOngoing = flowOf(true),
            )
        runCurrent()
        assertDuringProgress(progressFlow.value)

        progressFlow.value = 0.2f
        runCurrent()
        assertDuringProgress(progressFlow.value)

        progressFlow.value = 0.6f
        runCurrent()
        assertDuringProgress(progressFlow.value)

        progressFlow.value = 1f
        runCurrent()
        assertDuringProgress(progressFlow.value)

        transitionState.value = ObservableTransitionState.Idle(toScene)
        fakeSceneDataSource.changeScene(toScene)
        runCurrent()
        assertDuringProgress(progressFlow.value)

        assertThat(currentScene).isEqualTo(toScene)
    }

    companion object {
        private const val DEFAULT_DRAG_DOWN_AMOUNT = 123f
        private val DEFAULT_EXPANSION_EVENT =
@@ -70,6 +210,7 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
                fraction = 0.5f,
                expanded = true,
                tracking = true,
                dragDownPxAmount = DEFAULT_DRAG_DOWN_AMOUNT)
                dragDownPxAmount = DEFAULT_DRAG_DOWN_AMOUNT
            )
    }
}
+30 −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.stack.ui.viewmodel

import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.scene.domain.interactor.PanelExpansionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.data.repository.shadeRepository

val Kosmos.panelExpansionInteractor by Fixture {
    PanelExpansionInteractor(
        sceneInteractor = sceneInteractor,
        shadeRepository = shadeRepository,
    )
}