Loading packages/SystemUI/multivalentTests/src/com/android/systemui/actioncorner/data/repository/ActionCornerSettingRepositoryTest.kt 0 → 100644 +119 −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.actioncorner.data.repository import android.provider.Settings.Secure.ACTION_CORNER_ACTION_HOME import android.provider.Settings.Secure.ACTION_CORNER_ACTION_NOTIFICATIONS import android.provider.Settings.Secure.ACTION_CORNER_ACTION_OVERVIEW import android.provider.Settings.Secure.ACTION_CORNER_ACTION_QUICK_SETTINGS import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_RIGHT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_RIGHT_ACTION import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.actioncorner.data.model.ActionType.HOME import com.android.systemui.actioncorner.data.model.ActionType.NONE import com.android.systemui.actioncorner.data.model.ActionType.NOTIFICATIONS import com.android.systemui.actioncorner.data.model.ActionType.OVERVIEW import com.android.systemui.actioncorner.data.model.ActionType.QUICK_SETTINGS import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.testKosmos import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository import com.google.common.truth.Truth.assertThat import kotlin.test.Test import org.junit.Rule import org.junit.runner.RunWith import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class ActionCornerSettingRepositoryTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val settingsRepository = kosmos.userAwareSecureSettingsRepository private val Kosmos.underTest by Fixture { ActionCornerSettingRepository(settingsRepository, testScope.backgroundScope, testDispatcher) } @Test fun allCornersDefaultToNoneAction() = kosmos.runTest { val cornerActions = listOf( underTest.topLeftCornerAction, underTest.topRightCornerAction, underTest.bottomLeftCornerAction, underTest.bottomRightCornerAction, ) cornerActions.forEach { val model by collectLastValue(it) assertThat(model).isEqualTo(NONE) } } @Test fun testNotificationsActionOnTopLeftCorner() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_LEFT_ACTION, ACTION_CORNER_ACTION_NOTIFICATIONS, ) val model by collectLastValue(underTest.topLeftCornerAction) assertThat(model).isEqualTo(NOTIFICATIONS) } @Test fun testQuickSettingsActionOnTopRightCorner() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_RIGHT_ACTION, ACTION_CORNER_ACTION_QUICK_SETTINGS, ) val model by collectLastValue(underTest.topRightCornerAction) assertThat(model).isEqualTo(QUICK_SETTINGS) } @Test fun testOverviewActionOnBottomLeftCorner() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_BOTTOM_LEFT_ACTION, ACTION_CORNER_ACTION_OVERVIEW, ) val model by collectLastValue(underTest.bottomLeftCornerAction) assertThat(model).isEqualTo(OVERVIEW) } @Test fun testHomeActionOnBottomRightCorner() = kosmos.runTest { settingsRepository.setInt(ACTION_CORNER_BOTTOM_RIGHT_ACTION, ACTION_CORNER_ACTION_HOME) val model by collectLastValue(underTest.bottomRightCornerAction) assertThat(model).isEqualTo(HOME) } } packages/SystemUI/multivalentTests/src/com/android/systemui/actioncorner/domain/interactor/ActionCornerInteractorTest.kt +45 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,14 @@ package com.android.systemui.actioncorner.domain.interactor import android.provider.Settings.Secure.ACTION_CORNER_ACTION_HOME import android.provider.Settings.Secure.ACTION_CORNER_ACTION_NOTIFICATIONS import android.provider.Settings.Secure.ACTION_CORNER_ACTION_OVERVIEW import android.provider.Settings.Secure.ACTION_CORNER_ACTION_QUICK_SETTINGS import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_RIGHT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_RIGHT_ACTION import android.view.Display.DEFAULT_DISPLAY import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest Loading @@ -25,11 +33,13 @@ import com.android.systemui.actioncorner.data.model.ActionCornerRegion import com.android.systemui.actioncorner.data.model.ActionCornerRegion.BOTTOM_LEFT import com.android.systemui.actioncorner.data.model.ActionCornerRegion.BOTTOM_RIGHT import com.android.systemui.actioncorner.data.model.ActionCornerState.ActiveActionCorner import com.android.systemui.actioncorner.data.repository.ActionCornerSettingRepository import com.android.systemui.actioncorner.data.repository.FakeActionCornerRepository import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.lifecycle.activateIn Loading @@ -42,6 +52,7 @@ import com.android.systemui.shade.shadeTestUtil import com.android.systemui.shared.system.actioncorner.ActionCornerConstants.HOME import com.android.systemui.shared.system.actioncorner.ActionCornerConstants.OVERVIEW import com.android.systemui.testKosmos import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository import com.google.common.truth.Truth.assertThat import kotlin.test.Test import org.junit.Before Loading @@ -55,6 +66,12 @@ import org.mockito.kotlin.verify class ActionCornerInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val Kosmos.actionCornerRepository by Fixture { FakeActionCornerRepository() } private val settingsRepository = kosmos.userAwareSecureSettingsRepository private val Kosmos.actionCornerSettingRepository by Fixture { ActionCornerSettingRepository(settingsRepository, testScope.backgroundScope, testDispatcher) } private val Kosmos.launcherProxyService by Fixture { mock<LauncherProxyService>() } private val Kosmos.underTest by Fixture { ActionCornerInteractor( Loading @@ -63,6 +80,7 @@ class ActionCornerInteractorTest : SysuiTestCase() { launcherProxyService, shadeModeInteractor, shadeInteractor, actionCornerSettingRepository, ) } Loading @@ -73,22 +91,31 @@ class ActionCornerInteractorTest : SysuiTestCase() { } @Test fun bottomLeftCornerActivated_notifyLauncherOfOverviewAction() = fun bottomLeftCornerActivated_overviewActionConfigured_notifyLauncherOfOverviewAction() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_BOTTOM_LEFT_ACTION, ACTION_CORNER_ACTION_OVERVIEW, ) actionCornerRepository.addState(ActiveActionCorner(BOTTOM_LEFT, DEFAULT_DISPLAY)) verify(launcherProxyService).onActionCornerActivated(OVERVIEW, DEFAULT_DISPLAY) } @Test fun bottomRightCornerActivated_notifyLauncherOfHomeAction() = fun bottomRightCornerActivated_homeActionConfigured_notifyLauncherOfHomeAction() = kosmos.runTest { settingsRepository.setInt(ACTION_CORNER_BOTTOM_RIGHT_ACTION, ACTION_CORNER_ACTION_HOME) actionCornerRepository.addState(ActiveActionCorner(BOTTOM_RIGHT, DEFAULT_DISPLAY)) verify(launcherProxyService).onActionCornerActivated(HOME, DEFAULT_DISPLAY) } @Test fun shadeCollapsed_topLeftCornerActivated_expandNotificationShade() = fun shadeCollapsed_topLeftCornerActivated_notificationsActionConfigured_expandNotificationShade() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_LEFT_ACTION, ACTION_CORNER_ACTION_NOTIFICATIONS, ) shadeTestUtil.setShadeExpansion(0f) actionCornerRepository.addState( Loading @@ -100,8 +127,12 @@ class ActionCornerInteractorTest : SysuiTestCase() { } @Test fun shadeExpanded_topLeftCornerActivated_collapseNotificationShade() = fun shadeExpanded_topLeftCornerActivated_notificationsActionConfigured_collapseNotificationShade() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_LEFT_ACTION, ACTION_CORNER_ACTION_NOTIFICATIONS, ) shadeTestUtil.setShadeExpansion(1f) actionCornerRepository.addState( Loading @@ -113,8 +144,12 @@ class ActionCornerInteractorTest : SysuiTestCase() { } @Test fun qsCollapsed_topRightCornerActivated_expandQsPanel() = fun qsCollapsed_topRightCornerActivated_qsActionConfigured_expandQsPanel() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_RIGHT_ACTION, ACTION_CORNER_ACTION_QUICK_SETTINGS, ) shadeTestUtil.setQsExpansion(0f) actionCornerRepository.addState( Loading @@ -126,8 +161,12 @@ class ActionCornerInteractorTest : SysuiTestCase() { } @Test fun qsExpanded_topRightCornerActivated_collapseQsPanel() = fun qsExpanded_topRightCornerActivated_qsActionConfigured_collapseQsPanel() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_RIGHT_ACTION, ACTION_CORNER_ACTION_QUICK_SETTINGS, ) shadeTestUtil.setQsExpansion(1f) actionCornerRepository.addState( Loading packages/SystemUI/src/com/android/systemui/actioncorner/ActionCornerModule.kt +2 −1 Original line number Diff line number Diff line Loading @@ -26,13 +26,14 @@ import com.android.systemui.cursorposition.data.repository.MultiDisplayCursorPos import com.android.systemui.cursorposition.data.repository.SingleDisplayCursorPositionRepository import com.android.systemui.cursorposition.data.repository.SingleDisplayCursorPositionRepositoryFactory import com.android.systemui.dagger.SysUISingleton import com.android.systemui.settings.UserSettingsRepositoryModule import dagger.Binds import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap @Module @Module(includes = [UserSettingsRepositoryModule::class]) abstract class ActionCornerModule { @Binds @IntoMap Loading packages/SystemUI/src/com/android/systemui/actioncorner/data/model/ActionCornerState.kt +12 −0 Original line number Diff line number Diff line Loading @@ -44,3 +44,15 @@ enum class ActionCornerRegion { BOTTOM_LEFT, BOTTOM_RIGHT, } /** * Indicates the action type configured for the action corner. For [NONE], it means there is no * action configured for that corner. */ enum class ActionType { NONE, HOME, OVERVIEW, NOTIFICATIONS, QUICK_SETTINGS, } packages/SystemUI/src/com/android/systemui/actioncorner/data/repository/ActionCornerSettingRepository.kt 0 → 100644 +85 −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.actioncorner.data.repository import android.provider.Settings.Secure.ACTION_CORNER_ACTION_HOME import android.provider.Settings.Secure.ACTION_CORNER_ACTION_NOTIFICATIONS import android.provider.Settings.Secure.ACTION_CORNER_ACTION_OVERVIEW import android.provider.Settings.Secure.ACTION_CORNER_ACTION_QUICK_SETTINGS import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_RIGHT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_RIGHT_ACTION import android.provider.Settings.Secure.ActionCornerActionType import com.android.systemui.actioncorner.data.model.ActionType import com.android.systemui.actioncorner.data.model.ActionType.HOME import com.android.systemui.actioncorner.data.model.ActionType.NONE import com.android.systemui.actioncorner.data.model.ActionType.NOTIFICATIONS import com.android.systemui.actioncorner.data.model.ActionType.OVERVIEW import com.android.systemui.actioncorner.data.model.ActionType.QUICK_SETTINGS import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn /** * Repository for action configured for each action corner.Reads corresponding settings from * [UserAwareSecureSettingsRepository] and convert it to action type for each corner. */ class ActionCornerSettingRepository @Inject constructor( private val settingsRepository: UserAwareSecureSettingsRepository, @Background private val backgroundScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) { val topLeftCornerAction: StateFlow<ActionType> = getCornerActionFlow(ACTION_CORNER_TOP_LEFT_ACTION) val topRightCornerAction: StateFlow<ActionType> = getCornerActionFlow(ACTION_CORNER_TOP_RIGHT_ACTION) val bottomLeftCornerAction: StateFlow<ActionType> = getCornerActionFlow(ACTION_CORNER_BOTTOM_LEFT_ACTION) val bottomRightCornerAction: StateFlow<ActionType> = getCornerActionFlow(ACTION_CORNER_BOTTOM_RIGHT_ACTION) private fun getCornerActionFlow(settingName: String): StateFlow<ActionType> { return settingsRepository .intSetting(name = settingName) .map(::actionMapper) .flowOn(backgroundDispatcher) // Start it eagerly to avoid latency on reading settings when user hits the corner .stateIn(backgroundScope, started = SharingStarted.Eagerly, initialValue = NONE) } private fun actionMapper(@ActionCornerActionType action: Int): ActionType = when (action) { ACTION_CORNER_ACTION_HOME -> HOME ACTION_CORNER_ACTION_OVERVIEW -> OVERVIEW ACTION_CORNER_ACTION_NOTIFICATIONS -> NOTIFICATIONS ACTION_CORNER_ACTION_QUICK_SETTINGS -> QUICK_SETTINGS else -> NONE } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/actioncorner/data/repository/ActionCornerSettingRepositoryTest.kt 0 → 100644 +119 −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.actioncorner.data.repository import android.provider.Settings.Secure.ACTION_CORNER_ACTION_HOME import android.provider.Settings.Secure.ACTION_CORNER_ACTION_NOTIFICATIONS import android.provider.Settings.Secure.ACTION_CORNER_ACTION_OVERVIEW import android.provider.Settings.Secure.ACTION_CORNER_ACTION_QUICK_SETTINGS import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_RIGHT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_RIGHT_ACTION import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.actioncorner.data.model.ActionType.HOME import com.android.systemui.actioncorner.data.model.ActionType.NONE import com.android.systemui.actioncorner.data.model.ActionType.NOTIFICATIONS import com.android.systemui.actioncorner.data.model.ActionType.OVERVIEW import com.android.systemui.actioncorner.data.model.ActionType.QUICK_SETTINGS import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.testKosmos import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository import com.google.common.truth.Truth.assertThat import kotlin.test.Test import org.junit.Rule import org.junit.runner.RunWith import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class ActionCornerSettingRepositoryTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val settingsRepository = kosmos.userAwareSecureSettingsRepository private val Kosmos.underTest by Fixture { ActionCornerSettingRepository(settingsRepository, testScope.backgroundScope, testDispatcher) } @Test fun allCornersDefaultToNoneAction() = kosmos.runTest { val cornerActions = listOf( underTest.topLeftCornerAction, underTest.topRightCornerAction, underTest.bottomLeftCornerAction, underTest.bottomRightCornerAction, ) cornerActions.forEach { val model by collectLastValue(it) assertThat(model).isEqualTo(NONE) } } @Test fun testNotificationsActionOnTopLeftCorner() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_LEFT_ACTION, ACTION_CORNER_ACTION_NOTIFICATIONS, ) val model by collectLastValue(underTest.topLeftCornerAction) assertThat(model).isEqualTo(NOTIFICATIONS) } @Test fun testQuickSettingsActionOnTopRightCorner() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_RIGHT_ACTION, ACTION_CORNER_ACTION_QUICK_SETTINGS, ) val model by collectLastValue(underTest.topRightCornerAction) assertThat(model).isEqualTo(QUICK_SETTINGS) } @Test fun testOverviewActionOnBottomLeftCorner() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_BOTTOM_LEFT_ACTION, ACTION_CORNER_ACTION_OVERVIEW, ) val model by collectLastValue(underTest.bottomLeftCornerAction) assertThat(model).isEqualTo(OVERVIEW) } @Test fun testHomeActionOnBottomRightCorner() = kosmos.runTest { settingsRepository.setInt(ACTION_CORNER_BOTTOM_RIGHT_ACTION, ACTION_CORNER_ACTION_HOME) val model by collectLastValue(underTest.bottomRightCornerAction) assertThat(model).isEqualTo(HOME) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/actioncorner/domain/interactor/ActionCornerInteractorTest.kt +45 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,14 @@ package com.android.systemui.actioncorner.domain.interactor import android.provider.Settings.Secure.ACTION_CORNER_ACTION_HOME import android.provider.Settings.Secure.ACTION_CORNER_ACTION_NOTIFICATIONS import android.provider.Settings.Secure.ACTION_CORNER_ACTION_OVERVIEW import android.provider.Settings.Secure.ACTION_CORNER_ACTION_QUICK_SETTINGS import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_RIGHT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_RIGHT_ACTION import android.view.Display.DEFAULT_DISPLAY import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest Loading @@ -25,11 +33,13 @@ import com.android.systemui.actioncorner.data.model.ActionCornerRegion import com.android.systemui.actioncorner.data.model.ActionCornerRegion.BOTTOM_LEFT import com.android.systemui.actioncorner.data.model.ActionCornerRegion.BOTTOM_RIGHT import com.android.systemui.actioncorner.data.model.ActionCornerState.ActiveActionCorner import com.android.systemui.actioncorner.data.repository.ActionCornerSettingRepository import com.android.systemui.actioncorner.data.repository.FakeActionCornerRepository import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.lifecycle.activateIn Loading @@ -42,6 +52,7 @@ import com.android.systemui.shade.shadeTestUtil import com.android.systemui.shared.system.actioncorner.ActionCornerConstants.HOME import com.android.systemui.shared.system.actioncorner.ActionCornerConstants.OVERVIEW import com.android.systemui.testKosmos import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository import com.google.common.truth.Truth.assertThat import kotlin.test.Test import org.junit.Before Loading @@ -55,6 +66,12 @@ import org.mockito.kotlin.verify class ActionCornerInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val Kosmos.actionCornerRepository by Fixture { FakeActionCornerRepository() } private val settingsRepository = kosmos.userAwareSecureSettingsRepository private val Kosmos.actionCornerSettingRepository by Fixture { ActionCornerSettingRepository(settingsRepository, testScope.backgroundScope, testDispatcher) } private val Kosmos.launcherProxyService by Fixture { mock<LauncherProxyService>() } private val Kosmos.underTest by Fixture { ActionCornerInteractor( Loading @@ -63,6 +80,7 @@ class ActionCornerInteractorTest : SysuiTestCase() { launcherProxyService, shadeModeInteractor, shadeInteractor, actionCornerSettingRepository, ) } Loading @@ -73,22 +91,31 @@ class ActionCornerInteractorTest : SysuiTestCase() { } @Test fun bottomLeftCornerActivated_notifyLauncherOfOverviewAction() = fun bottomLeftCornerActivated_overviewActionConfigured_notifyLauncherOfOverviewAction() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_BOTTOM_LEFT_ACTION, ACTION_CORNER_ACTION_OVERVIEW, ) actionCornerRepository.addState(ActiveActionCorner(BOTTOM_LEFT, DEFAULT_DISPLAY)) verify(launcherProxyService).onActionCornerActivated(OVERVIEW, DEFAULT_DISPLAY) } @Test fun bottomRightCornerActivated_notifyLauncherOfHomeAction() = fun bottomRightCornerActivated_homeActionConfigured_notifyLauncherOfHomeAction() = kosmos.runTest { settingsRepository.setInt(ACTION_CORNER_BOTTOM_RIGHT_ACTION, ACTION_CORNER_ACTION_HOME) actionCornerRepository.addState(ActiveActionCorner(BOTTOM_RIGHT, DEFAULT_DISPLAY)) verify(launcherProxyService).onActionCornerActivated(HOME, DEFAULT_DISPLAY) } @Test fun shadeCollapsed_topLeftCornerActivated_expandNotificationShade() = fun shadeCollapsed_topLeftCornerActivated_notificationsActionConfigured_expandNotificationShade() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_LEFT_ACTION, ACTION_CORNER_ACTION_NOTIFICATIONS, ) shadeTestUtil.setShadeExpansion(0f) actionCornerRepository.addState( Loading @@ -100,8 +127,12 @@ class ActionCornerInteractorTest : SysuiTestCase() { } @Test fun shadeExpanded_topLeftCornerActivated_collapseNotificationShade() = fun shadeExpanded_topLeftCornerActivated_notificationsActionConfigured_collapseNotificationShade() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_LEFT_ACTION, ACTION_CORNER_ACTION_NOTIFICATIONS, ) shadeTestUtil.setShadeExpansion(1f) actionCornerRepository.addState( Loading @@ -113,8 +144,12 @@ class ActionCornerInteractorTest : SysuiTestCase() { } @Test fun qsCollapsed_topRightCornerActivated_expandQsPanel() = fun qsCollapsed_topRightCornerActivated_qsActionConfigured_expandQsPanel() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_RIGHT_ACTION, ACTION_CORNER_ACTION_QUICK_SETTINGS, ) shadeTestUtil.setQsExpansion(0f) actionCornerRepository.addState( Loading @@ -126,8 +161,12 @@ class ActionCornerInteractorTest : SysuiTestCase() { } @Test fun qsExpanded_topRightCornerActivated_collapseQsPanel() = fun qsExpanded_topRightCornerActivated_qsActionConfigured_collapseQsPanel() = kosmos.runTest { settingsRepository.setInt( ACTION_CORNER_TOP_RIGHT_ACTION, ACTION_CORNER_ACTION_QUICK_SETTINGS, ) shadeTestUtil.setQsExpansion(1f) actionCornerRepository.addState( Loading
packages/SystemUI/src/com/android/systemui/actioncorner/ActionCornerModule.kt +2 −1 Original line number Diff line number Diff line Loading @@ -26,13 +26,14 @@ import com.android.systemui.cursorposition.data.repository.MultiDisplayCursorPos import com.android.systemui.cursorposition.data.repository.SingleDisplayCursorPositionRepository import com.android.systemui.cursorposition.data.repository.SingleDisplayCursorPositionRepositoryFactory import com.android.systemui.dagger.SysUISingleton import com.android.systemui.settings.UserSettingsRepositoryModule import dagger.Binds import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap @Module @Module(includes = [UserSettingsRepositoryModule::class]) abstract class ActionCornerModule { @Binds @IntoMap Loading
packages/SystemUI/src/com/android/systemui/actioncorner/data/model/ActionCornerState.kt +12 −0 Original line number Diff line number Diff line Loading @@ -44,3 +44,15 @@ enum class ActionCornerRegion { BOTTOM_LEFT, BOTTOM_RIGHT, } /** * Indicates the action type configured for the action corner. For [NONE], it means there is no * action configured for that corner. */ enum class ActionType { NONE, HOME, OVERVIEW, NOTIFICATIONS, QUICK_SETTINGS, }
packages/SystemUI/src/com/android/systemui/actioncorner/data/repository/ActionCornerSettingRepository.kt 0 → 100644 +85 −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.actioncorner.data.repository import android.provider.Settings.Secure.ACTION_CORNER_ACTION_HOME import android.provider.Settings.Secure.ACTION_CORNER_ACTION_NOTIFICATIONS import android.provider.Settings.Secure.ACTION_CORNER_ACTION_OVERVIEW import android.provider.Settings.Secure.ACTION_CORNER_ACTION_QUICK_SETTINGS import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_BOTTOM_RIGHT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_LEFT_ACTION import android.provider.Settings.Secure.ACTION_CORNER_TOP_RIGHT_ACTION import android.provider.Settings.Secure.ActionCornerActionType import com.android.systemui.actioncorner.data.model.ActionType import com.android.systemui.actioncorner.data.model.ActionType.HOME import com.android.systemui.actioncorner.data.model.ActionType.NONE import com.android.systemui.actioncorner.data.model.ActionType.NOTIFICATIONS import com.android.systemui.actioncorner.data.model.ActionType.OVERVIEW import com.android.systemui.actioncorner.data.model.ActionType.QUICK_SETTINGS import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn /** * Repository for action configured for each action corner.Reads corresponding settings from * [UserAwareSecureSettingsRepository] and convert it to action type for each corner. */ class ActionCornerSettingRepository @Inject constructor( private val settingsRepository: UserAwareSecureSettingsRepository, @Background private val backgroundScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) { val topLeftCornerAction: StateFlow<ActionType> = getCornerActionFlow(ACTION_CORNER_TOP_LEFT_ACTION) val topRightCornerAction: StateFlow<ActionType> = getCornerActionFlow(ACTION_CORNER_TOP_RIGHT_ACTION) val bottomLeftCornerAction: StateFlow<ActionType> = getCornerActionFlow(ACTION_CORNER_BOTTOM_LEFT_ACTION) val bottomRightCornerAction: StateFlow<ActionType> = getCornerActionFlow(ACTION_CORNER_BOTTOM_RIGHT_ACTION) private fun getCornerActionFlow(settingName: String): StateFlow<ActionType> { return settingsRepository .intSetting(name = settingName) .map(::actionMapper) .flowOn(backgroundDispatcher) // Start it eagerly to avoid latency on reading settings when user hits the corner .stateIn(backgroundScope, started = SharingStarted.Eagerly, initialValue = NONE) } private fun actionMapper(@ActionCornerActionType action: Int): ActionType = when (action) { ACTION_CORNER_ACTION_HOME -> HOME ACTION_CORNER_ACTION_OVERVIEW -> OVERVIEW ACTION_CORNER_ACTION_NOTIFICATIONS -> NOTIFICATIONS ACTION_CORNER_ACTION_QUICK_SETTINGS -> QUICK_SETTINGS else -> NONE } }