Loading packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryTest.kt +179 −14 Original line number Diff line number Diff line Loading @@ -16,9 +16,13 @@ package com.android.systemui.topwindoweffects.data.repository import android.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager import android.content.pm.UserInfo import android.hardware.input.InputManager import android.os.Bundle import android.os.Handler import android.os.UserHandle import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.provider.Settings.Global.POWER_BUTTON_LONG_PRESS Loading @@ -33,15 +37,22 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.shared.Flags import com.android.systemui.testKosmos import com.android.systemui.topwindoweffects.data.repository.SqueezeEffectRepositoryImpl.Companion.IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE import com.android.systemui.topwindoweffects.data.repository.SqueezeEffectRepositoryImpl.Companion.IS_INVOCATION_EFFECT_ENABLED_KEY import com.android.systemui.topwindoweffects.data.repository.SqueezeEffectRepositoryImpl.Companion.SET_INVOCATION_EFFECT_PARAMETERS_ACTION import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.settings.FakeGlobalSettings import com.google.common.truth.Truth.assertThat import java.util.concurrent.Executor import kotlinx.coroutines.test.StandardTestDispatcher import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.eq import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations private fun createAssistantSettingBundle(enableAssistantSetting: Boolean) = Loading @@ -56,60 +67,66 @@ class SqueezeEffectRepositoryTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val globalSettings = FakeGlobalSettings(StandardTestDispatcher()) private val mainExecutor = Executor(Runnable::run) private val userRepository = FakeUserRepository() @Mock private lateinit var bgHandler: Handler @Mock private lateinit var handler: Handler @Mock private lateinit var inputManager: InputManager @Mock private lateinit var roleManager: RoleManager private val onRoleHoldersChangedListener = ArgumentCaptor.forClass(OnRoleHoldersChangedListener::class.java) private val Kosmos.underTest by Kosmos.Fixture { SqueezeEffectRepositoryImpl( context = mContext, handler = bgHandler, coroutineContext = testScope.testScheduler, executor = Runnable::run, inputManager = inputManager, context = context, coroutineScope = testScope.backgroundScope, globalSettings = globalSettings, userRepository = userRepository, inputManager = inputManager, handler = handler, coroutineContext = testScope.testScheduler, roleManager = roleManager, executor = mainExecutor, ) } @Before fun setup() { MockitoAnnotations.initMocks(this) MockitoAnnotations.openMocks(this) } @DisableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testSqueezeEffectDisabled_WhenOtherwiseEnabled_FlagDisabled() = fun testSqueezeEffectDisabled_FlagDisabled() = kosmos.runTest { globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints(createAssistantSettingBundle(true)) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled).isFalse() } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testSqueezeEffectDisabled_WhenOtherwiseEnabled_GlobalSettingDisabled() = fun testSqueezeEffectDisabled_GlobalSettingDisabled() = kosmos.runTest { underTest.tryHandleSetUiHints(createAssistantSettingBundle(true)) globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 0) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled).isFalse() } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testSqueezeEffectDisabled_WhenOtherwiseEnabled_AssistantSettingDisabled() = fun testSqueezeEffectDisabled_AssistantSettingDisabled() = kosmos.runTest { globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints(createAssistantSettingBundle(false)) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled).isFalse() } Loading @@ -121,7 +138,155 @@ class SqueezeEffectRepositoryTest : SysuiTestCase() { underTest.tryHandleSetUiHints(createAssistantSettingBundle(true)) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled).isTrue() } private suspend fun Kosmos.initUserAndAssistant( userInfos: List<UserInfo>, userIndex: Int, assistantName: String, ) { underTest // "poke" class to ensure it's initialized userRepository.setUserInfos(userInfos) userRepository.setSelectedUserInfo(userInfos[userIndex]) verify(roleManager) .addOnRoleHoldersChangedListenerAsUser( eq(mainExecutor), onRoleHoldersChangedListener.capture(), eq(UserHandle.ALL), ) `when`( roleManager.getRoleHoldersAsUser( eq(RoleManager.ROLE_ASSISTANT), eq(userInfos[userIndex].userHandle), ) ) .thenReturn(listOf(assistantName)) onRoleHoldersChangedListener.value.onRoleHoldersChanged( RoleManager.ROLE_ASSISTANT, userInfos[userIndex].userHandle, ) } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testAssistantEnabledStatusIsDefault_AssistantSwitched() = kosmos.runTest { initUserAndAssistant(userInfos, 0, "a") globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints( createAssistantSettingBundle( !IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE ) ) `when`( roleManager.getRoleHoldersAsUser( eq(RoleManager.ROLE_ASSISTANT), eq(userInfos[0].userHandle), ) ) .thenReturn(listOf("b")) onRoleHoldersChangedListener.value.onRoleHoldersChanged( RoleManager.ROLE_ASSISTANT, userInfos[0].userHandle, ) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled) .isEqualTo(IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE) } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testAssistantEnabledStatusIsDefault_UserSwitched() = kosmos.runTest { initUserAndAssistant(userInfos, 0, "a") globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints( createAssistantSettingBundle( !IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE ) ) userRepository.setSelectedUserInfo(userInfos[1]) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled) .isEqualTo(IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE) } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testAssistantEnabledStatusIsRetained_AssistantSwitchedBackAndForth() = kosmos.runTest { initUserAndAssistant(userInfos, 0, "a") globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints( createAssistantSettingBundle( !IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE ) ) `when`( roleManager.getRoleHoldersAsUser( eq(RoleManager.ROLE_ASSISTANT), eq(UserHandle.CURRENT), ) ) .thenReturn(listOf("b")) onRoleHoldersChangedListener.value.onRoleHoldersChanged( RoleManager.ROLE_ASSISTANT, UserHandle.CURRENT, ) `when`( roleManager.getRoleHoldersAsUser( eq(RoleManager.ROLE_ASSISTANT), eq(UserHandle.CURRENT), ) ) .thenReturn(listOf("a")) onRoleHoldersChangedListener.value.onRoleHoldersChanged( RoleManager.ROLE_ASSISTANT, UserHandle.CURRENT, ) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled) .isEqualTo(!IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE) } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testAssistantEnabledStatusIsRetained_UserSwitchedBackAndForth() = kosmos.runTest { initUserAndAssistant(userInfos, 0, "a") globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints( createAssistantSettingBundle( !IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE ) ) userRepository.setSelectedUserInfo(userInfos[1]) userRepository.setSelectedUserInfo(userInfos[0]) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled) .isEqualTo(!IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE) } companion object { private val userInfos = listOf( UserInfo().apply { id = 0 name = "User 0" }, UserInfo().apply { id = 1 name = "User 1" }, ) } } packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryImpl.kt +133 −7 Original line number Diff line number Diff line Loading @@ -17,12 +17,15 @@ package com.android.systemui.topwindoweffects.data.repository import android.annotation.SuppressLint import android.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager import android.content.Context import android.database.ContentObserver import android.hardware.input.InputManager import android.hardware.input.KeyGestureEvent import android.os.Bundle import android.os.Handler import android.os.UserHandle import android.provider.Settings.Global.POWER_BUTTON_LONG_PRESS import android.provider.Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS import android.util.DisplayUtils Loading @@ -30,6 +33,7 @@ import android.view.DisplayInfo import android.view.KeyEvent import androidx.annotation.ArrayRes import androidx.annotation.DrawableRes import androidx.core.content.edit import com.android.internal.annotations.VisibleForTesting import com.android.systemui.assist.AssistManager import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging Loading @@ -39,30 +43,97 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.res.R import com.android.systemui.shared.Flags import com.android.systemui.topwindoweffects.data.entity.SqueezeEffectCornersInfo import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import java.util.concurrent.Executor import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @SysUISingleton class SqueezeEffectRepositoryImpl @Inject constructor( @Application private val context: Context, @Background private val handler: Handler?, @Background private val coroutineContext: CoroutineContext, @Background executor: Executor, @Background private val coroutineScope: CoroutineScope, private val globalSettings: GlobalSettings, private val userRepository: UserRepository, private val inputManager: InputManager, @Background handler: Handler?, @Background coroutineContext: CoroutineContext, roleManager: RoleManager, @Background executor: Executor, ) : SqueezeEffectRepository, InvocationEffectSetUiHintsHandler { private val sharedPreferences by lazy { context.getSharedPreferences(SHARED_PREFERENCES_FILE_NAME, Context.MODE_PRIVATE) } private val isInvocationEffectEnabledByAssistantFlow = MutableStateFlow<Boolean?>(null) private val selectedAssistantName: StateFlow<String> = conflatedCallbackFlow { val listener = OnRoleHoldersChangedListener { roleName, _ -> if (roleName == RoleManager.ROLE_ASSISTANT) { trySendWithFailureLogging( roleManager.getCurrentAssistantFor(userRepository.selectedUserHandle), TAG, "updated currentlyActiveAssistantName due to role change", ) } } roleManager.addOnRoleHoldersChangedListenerAsUser( executor, listener, UserHandle.ALL, ) launch { userRepository.selectedUser.collect { trySendWithFailureLogging( roleManager.getCurrentAssistantFor(userRepository.selectedUserHandle), TAG, "updated currentlyActiveAssistantName due to user change", ) } } awaitClose { roleManager.removeOnRoleHoldersChangedListenerAsUser(listener, UserHandle.ALL) } } .flowOn(coroutineContext) .stateIn( scope = coroutineScope, started = SharingStarted.WhileSubscribed(), initialValue = roleManager.getCurrentAssistantFor(userRepository.selectedUserHandle), ) private val selectedAssistantNameAndUserFlow = selectedAssistantName .combine(userRepository.selectedUser) { a, b -> Pair(a, b) } .distinctUntilChanged() init { coroutineScope.launch { selectedAssistantNameAndUserFlow.collect { // Assistant or user changed, reload enabled state isInvocationEffectEnabledByAssistantFlow.value = loadIsInvocationEffectEnabledByAssistant() } } } private val isPowerButtonLongPressConfiguredToLaunchAssistantFlow: Flow<Boolean> = conflatedCallbackFlow { val observer = Loading Loading @@ -175,14 +246,38 @@ constructor( return drawableResource } private val isInvocationEffectEnabledForCurrentAssistantFlow = MutableStateFlow(true) private fun loadIsInvocationEffectEnabledByAssistant(): Boolean { val persistedForUser = sharedPreferences.getInt( PERSISTED_FOR_USER_PREFERENCE, PERSISTED_FOR_USER_DEFAULT_VALUE, ) val persistedForAssistant = sharedPreferences.getString( PERSISTED_FOR_ASSISTANT_PREFERENCE, PERSISTED_FOR_ASSISTANT_DEFAULT_VALUE, ) return if ( persistedForUser == userRepository.selectedUserHandle.identifier && persistedForAssistant == selectedAssistantName.value ) { sharedPreferences.getBoolean( IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_PREFERENCE, IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE, ) } else { IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE } } override val isSqueezeEffectEnabled: Flow<Boolean> = combine( isPowerButtonLongPressConfiguredToLaunchAssistantFlow, isInvocationEffectEnabledForCurrentAssistantFlow, isInvocationEffectEnabledByAssistantFlow, ) { prerequisites -> prerequisites.all { it } && Flags.enableLppAssistInvocationEffect() prerequisites.all { it ?: false } && Flags.enableLppAssistInvocationEffect() } private fun getIsPowerButtonLongPressConfiguredToLaunchAssistant() = Loading @@ -207,8 +302,9 @@ constructor( return when (hints.getString(AssistManager.ACTION_KEY)) { SET_INVOCATION_EFFECT_PARAMETERS_ACTION -> { if (hints.containsKey(IS_INVOCATION_EFFECT_ENABLED_KEY)) { isInvocationEffectEnabledForCurrentAssistantFlow.value = setIsInvocationEffectEnabledByAssistant( hints.getBoolean(IS_INVOCATION_EFFECT_ENABLED_KEY) ) } true } Loading @@ -216,6 +312,17 @@ constructor( } } private fun setIsInvocationEffectEnabledByAssistant(enabled: Boolean) { coroutineScope.launch { isInvocationEffectEnabledByAssistantFlow.value = enabled sharedPreferences.edit { putBoolean(IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_PREFERENCE, enabled) putString(PERSISTED_FOR_ASSISTANT_PREFERENCE, selectedAssistantName.value) putInt(PERSISTED_FOR_USER_PREFERENCE, userRepository.selectedUserHandle.identifier) } } } companion object { private const val TAG = "SqueezeEffectRepository" Loading @@ -229,9 +336,28 @@ constructor( */ @VisibleForTesting const val DEFAULT_INITIAL_DELAY_MILLIS = 150L @VisibleForTesting const val DEFAULT_LONG_PRESS_POWER_DURATION_MILLIS = 500L @VisibleForTesting const val SET_INVOCATION_EFFECT_PARAMETERS_ACTION = "set_invocation_effect_parameters" @VisibleForTesting const val IS_INVOCATION_EFFECT_ENABLED_KEY = "is_invocation_effect_enabled" @VisibleForTesting const val IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE = true private const val PERSISTED_FOR_USER_DEFAULT_VALUE = Integer.MIN_VALUE private const val PERSISTED_FOR_ASSISTANT_DEFAULT_VALUE = "" @VisibleForTesting const val SHARED_PREFERENCES_FILE_NAME = "assistant_invocation_effect_preferences" @VisibleForTesting const val IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_PREFERENCE = "is_invocation_effect_enabled" private const val PERSISTED_FOR_ASSISTANT_PREFERENCE = "persisted_for_assistant" private const val PERSISTED_FOR_USER_PREFERENCE = "persisted_for_user" } } private val UserRepository.selectedUserHandle get() = selectedUser.value.userInfo.userHandle private fun RoleManager.getCurrentAssistantFor(userHandle: UserHandle) = getRoleHoldersAsUser(RoleManager.ROLE_ASSISTANT, userHandle)?.firstOrNull() ?: "" Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryTest.kt +179 −14 Original line number Diff line number Diff line Loading @@ -16,9 +16,13 @@ package com.android.systemui.topwindoweffects.data.repository import android.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager import android.content.pm.UserInfo import android.hardware.input.InputManager import android.os.Bundle import android.os.Handler import android.os.UserHandle import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.provider.Settings.Global.POWER_BUTTON_LONG_PRESS Loading @@ -33,15 +37,22 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.shared.Flags import com.android.systemui.testKosmos import com.android.systemui.topwindoweffects.data.repository.SqueezeEffectRepositoryImpl.Companion.IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE import com.android.systemui.topwindoweffects.data.repository.SqueezeEffectRepositoryImpl.Companion.IS_INVOCATION_EFFECT_ENABLED_KEY import com.android.systemui.topwindoweffects.data.repository.SqueezeEffectRepositoryImpl.Companion.SET_INVOCATION_EFFECT_PARAMETERS_ACTION import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.settings.FakeGlobalSettings import com.google.common.truth.Truth.assertThat import java.util.concurrent.Executor import kotlinx.coroutines.test.StandardTestDispatcher import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.eq import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations private fun createAssistantSettingBundle(enableAssistantSetting: Boolean) = Loading @@ -56,60 +67,66 @@ class SqueezeEffectRepositoryTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val globalSettings = FakeGlobalSettings(StandardTestDispatcher()) private val mainExecutor = Executor(Runnable::run) private val userRepository = FakeUserRepository() @Mock private lateinit var bgHandler: Handler @Mock private lateinit var handler: Handler @Mock private lateinit var inputManager: InputManager @Mock private lateinit var roleManager: RoleManager private val onRoleHoldersChangedListener = ArgumentCaptor.forClass(OnRoleHoldersChangedListener::class.java) private val Kosmos.underTest by Kosmos.Fixture { SqueezeEffectRepositoryImpl( context = mContext, handler = bgHandler, coroutineContext = testScope.testScheduler, executor = Runnable::run, inputManager = inputManager, context = context, coroutineScope = testScope.backgroundScope, globalSettings = globalSettings, userRepository = userRepository, inputManager = inputManager, handler = handler, coroutineContext = testScope.testScheduler, roleManager = roleManager, executor = mainExecutor, ) } @Before fun setup() { MockitoAnnotations.initMocks(this) MockitoAnnotations.openMocks(this) } @DisableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testSqueezeEffectDisabled_WhenOtherwiseEnabled_FlagDisabled() = fun testSqueezeEffectDisabled_FlagDisabled() = kosmos.runTest { globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints(createAssistantSettingBundle(true)) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled).isFalse() } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testSqueezeEffectDisabled_WhenOtherwiseEnabled_GlobalSettingDisabled() = fun testSqueezeEffectDisabled_GlobalSettingDisabled() = kosmos.runTest { underTest.tryHandleSetUiHints(createAssistantSettingBundle(true)) globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 0) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled).isFalse() } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testSqueezeEffectDisabled_WhenOtherwiseEnabled_AssistantSettingDisabled() = fun testSqueezeEffectDisabled_AssistantSettingDisabled() = kosmos.runTest { globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints(createAssistantSettingBundle(false)) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled).isFalse() } Loading @@ -121,7 +138,155 @@ class SqueezeEffectRepositoryTest : SysuiTestCase() { underTest.tryHandleSetUiHints(createAssistantSettingBundle(true)) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled).isTrue() } private suspend fun Kosmos.initUserAndAssistant( userInfos: List<UserInfo>, userIndex: Int, assistantName: String, ) { underTest // "poke" class to ensure it's initialized userRepository.setUserInfos(userInfos) userRepository.setSelectedUserInfo(userInfos[userIndex]) verify(roleManager) .addOnRoleHoldersChangedListenerAsUser( eq(mainExecutor), onRoleHoldersChangedListener.capture(), eq(UserHandle.ALL), ) `when`( roleManager.getRoleHoldersAsUser( eq(RoleManager.ROLE_ASSISTANT), eq(userInfos[userIndex].userHandle), ) ) .thenReturn(listOf(assistantName)) onRoleHoldersChangedListener.value.onRoleHoldersChanged( RoleManager.ROLE_ASSISTANT, userInfos[userIndex].userHandle, ) } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testAssistantEnabledStatusIsDefault_AssistantSwitched() = kosmos.runTest { initUserAndAssistant(userInfos, 0, "a") globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints( createAssistantSettingBundle( !IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE ) ) `when`( roleManager.getRoleHoldersAsUser( eq(RoleManager.ROLE_ASSISTANT), eq(userInfos[0].userHandle), ) ) .thenReturn(listOf("b")) onRoleHoldersChangedListener.value.onRoleHoldersChanged( RoleManager.ROLE_ASSISTANT, userInfos[0].userHandle, ) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled) .isEqualTo(IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE) } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testAssistantEnabledStatusIsDefault_UserSwitched() = kosmos.runTest { initUserAndAssistant(userInfos, 0, "a") globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints( createAssistantSettingBundle( !IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE ) ) userRepository.setSelectedUserInfo(userInfos[1]) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled) .isEqualTo(IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE) } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testAssistantEnabledStatusIsRetained_AssistantSwitchedBackAndForth() = kosmos.runTest { initUserAndAssistant(userInfos, 0, "a") globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints( createAssistantSettingBundle( !IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE ) ) `when`( roleManager.getRoleHoldersAsUser( eq(RoleManager.ROLE_ASSISTANT), eq(UserHandle.CURRENT), ) ) .thenReturn(listOf("b")) onRoleHoldersChangedListener.value.onRoleHoldersChanged( RoleManager.ROLE_ASSISTANT, UserHandle.CURRENT, ) `when`( roleManager.getRoleHoldersAsUser( eq(RoleManager.ROLE_ASSISTANT), eq(UserHandle.CURRENT), ) ) .thenReturn(listOf("a")) onRoleHoldersChangedListener.value.onRoleHoldersChanged( RoleManager.ROLE_ASSISTANT, UserHandle.CURRENT, ) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled) .isEqualTo(!IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE) } @EnableFlags(Flags.FLAG_ENABLE_LPP_ASSIST_INVOCATION_EFFECT) @Test fun testAssistantEnabledStatusIsRetained_UserSwitchedBackAndForth() = kosmos.runTest { initUserAndAssistant(userInfos, 0, "a") globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5) underTest.tryHandleSetUiHints( createAssistantSettingBundle( !IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE ) ) userRepository.setSelectedUserInfo(userInfos[1]) userRepository.setSelectedUserInfo(userInfos[0]) val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled) assertThat(isSqueezeEffectEnabled) .isEqualTo(!IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE) } companion object { private val userInfos = listOf( UserInfo().apply { id = 0 name = "User 0" }, UserInfo().apply { id = 1 name = "User 1" }, ) } }
packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryImpl.kt +133 −7 Original line number Diff line number Diff line Loading @@ -17,12 +17,15 @@ package com.android.systemui.topwindoweffects.data.repository import android.annotation.SuppressLint import android.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager import android.content.Context import android.database.ContentObserver import android.hardware.input.InputManager import android.hardware.input.KeyGestureEvent import android.os.Bundle import android.os.Handler import android.os.UserHandle import android.provider.Settings.Global.POWER_BUTTON_LONG_PRESS import android.provider.Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS import android.util.DisplayUtils Loading @@ -30,6 +33,7 @@ import android.view.DisplayInfo import android.view.KeyEvent import androidx.annotation.ArrayRes import androidx.annotation.DrawableRes import androidx.core.content.edit import com.android.internal.annotations.VisibleForTesting import com.android.systemui.assist.AssistManager import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging Loading @@ -39,30 +43,97 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.res.R import com.android.systemui.shared.Flags import com.android.systemui.topwindoweffects.data.entity.SqueezeEffectCornersInfo import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import java.util.concurrent.Executor import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @SysUISingleton class SqueezeEffectRepositoryImpl @Inject constructor( @Application private val context: Context, @Background private val handler: Handler?, @Background private val coroutineContext: CoroutineContext, @Background executor: Executor, @Background private val coroutineScope: CoroutineScope, private val globalSettings: GlobalSettings, private val userRepository: UserRepository, private val inputManager: InputManager, @Background handler: Handler?, @Background coroutineContext: CoroutineContext, roleManager: RoleManager, @Background executor: Executor, ) : SqueezeEffectRepository, InvocationEffectSetUiHintsHandler { private val sharedPreferences by lazy { context.getSharedPreferences(SHARED_PREFERENCES_FILE_NAME, Context.MODE_PRIVATE) } private val isInvocationEffectEnabledByAssistantFlow = MutableStateFlow<Boolean?>(null) private val selectedAssistantName: StateFlow<String> = conflatedCallbackFlow { val listener = OnRoleHoldersChangedListener { roleName, _ -> if (roleName == RoleManager.ROLE_ASSISTANT) { trySendWithFailureLogging( roleManager.getCurrentAssistantFor(userRepository.selectedUserHandle), TAG, "updated currentlyActiveAssistantName due to role change", ) } } roleManager.addOnRoleHoldersChangedListenerAsUser( executor, listener, UserHandle.ALL, ) launch { userRepository.selectedUser.collect { trySendWithFailureLogging( roleManager.getCurrentAssistantFor(userRepository.selectedUserHandle), TAG, "updated currentlyActiveAssistantName due to user change", ) } } awaitClose { roleManager.removeOnRoleHoldersChangedListenerAsUser(listener, UserHandle.ALL) } } .flowOn(coroutineContext) .stateIn( scope = coroutineScope, started = SharingStarted.WhileSubscribed(), initialValue = roleManager.getCurrentAssistantFor(userRepository.selectedUserHandle), ) private val selectedAssistantNameAndUserFlow = selectedAssistantName .combine(userRepository.selectedUser) { a, b -> Pair(a, b) } .distinctUntilChanged() init { coroutineScope.launch { selectedAssistantNameAndUserFlow.collect { // Assistant or user changed, reload enabled state isInvocationEffectEnabledByAssistantFlow.value = loadIsInvocationEffectEnabledByAssistant() } } } private val isPowerButtonLongPressConfiguredToLaunchAssistantFlow: Flow<Boolean> = conflatedCallbackFlow { val observer = Loading Loading @@ -175,14 +246,38 @@ constructor( return drawableResource } private val isInvocationEffectEnabledForCurrentAssistantFlow = MutableStateFlow(true) private fun loadIsInvocationEffectEnabledByAssistant(): Boolean { val persistedForUser = sharedPreferences.getInt( PERSISTED_FOR_USER_PREFERENCE, PERSISTED_FOR_USER_DEFAULT_VALUE, ) val persistedForAssistant = sharedPreferences.getString( PERSISTED_FOR_ASSISTANT_PREFERENCE, PERSISTED_FOR_ASSISTANT_DEFAULT_VALUE, ) return if ( persistedForUser == userRepository.selectedUserHandle.identifier && persistedForAssistant == selectedAssistantName.value ) { sharedPreferences.getBoolean( IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_PREFERENCE, IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE, ) } else { IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE } } override val isSqueezeEffectEnabled: Flow<Boolean> = combine( isPowerButtonLongPressConfiguredToLaunchAssistantFlow, isInvocationEffectEnabledForCurrentAssistantFlow, isInvocationEffectEnabledByAssistantFlow, ) { prerequisites -> prerequisites.all { it } && Flags.enableLppAssistInvocationEffect() prerequisites.all { it ?: false } && Flags.enableLppAssistInvocationEffect() } private fun getIsPowerButtonLongPressConfiguredToLaunchAssistant() = Loading @@ -207,8 +302,9 @@ constructor( return when (hints.getString(AssistManager.ACTION_KEY)) { SET_INVOCATION_EFFECT_PARAMETERS_ACTION -> { if (hints.containsKey(IS_INVOCATION_EFFECT_ENABLED_KEY)) { isInvocationEffectEnabledForCurrentAssistantFlow.value = setIsInvocationEffectEnabledByAssistant( hints.getBoolean(IS_INVOCATION_EFFECT_ENABLED_KEY) ) } true } Loading @@ -216,6 +312,17 @@ constructor( } } private fun setIsInvocationEffectEnabledByAssistant(enabled: Boolean) { coroutineScope.launch { isInvocationEffectEnabledByAssistantFlow.value = enabled sharedPreferences.edit { putBoolean(IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_PREFERENCE, enabled) putString(PERSISTED_FOR_ASSISTANT_PREFERENCE, selectedAssistantName.value) putInt(PERSISTED_FOR_USER_PREFERENCE, userRepository.selectedUserHandle.identifier) } } } companion object { private const val TAG = "SqueezeEffectRepository" Loading @@ -229,9 +336,28 @@ constructor( */ @VisibleForTesting const val DEFAULT_INITIAL_DELAY_MILLIS = 150L @VisibleForTesting const val DEFAULT_LONG_PRESS_POWER_DURATION_MILLIS = 500L @VisibleForTesting const val SET_INVOCATION_EFFECT_PARAMETERS_ACTION = "set_invocation_effect_parameters" @VisibleForTesting const val IS_INVOCATION_EFFECT_ENABLED_KEY = "is_invocation_effect_enabled" @VisibleForTesting const val IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_DEFAULT_VALUE = true private const val PERSISTED_FOR_USER_DEFAULT_VALUE = Integer.MIN_VALUE private const val PERSISTED_FOR_ASSISTANT_DEFAULT_VALUE = "" @VisibleForTesting const val SHARED_PREFERENCES_FILE_NAME = "assistant_invocation_effect_preferences" @VisibleForTesting const val IS_INVOCATION_EFFECT_ENABLED_BY_ASSISTANT_PREFERENCE = "is_invocation_effect_enabled" private const val PERSISTED_FOR_ASSISTANT_PREFERENCE = "persisted_for_assistant" private const val PERSISTED_FOR_USER_PREFERENCE = "persisted_for_user" } } private val UserRepository.selectedUserHandle get() = selectedUser.value.userInfo.userHandle private fun RoleManager.getCurrentAssistantFor(userHandle: UserHandle) = getRoleHoldersAsUser(RoleManager.ROLE_ASSISTANT, userHandle)?.firstOrNull() ?: ""