Loading packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +74 −1 Original line number Diff line number Diff line Loading @@ -52,10 +52,12 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan Loading @@ -75,12 +77,15 @@ import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilledIconButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButtonColors import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State Loading Loading @@ -153,7 +158,7 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialogFactory import kotlinx.coroutines.launch @OptIn(ExperimentalComposeUiApi::class) @OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) @Composable fun CommunalHub( modifier: Modifier = Modifier, Loading Loading @@ -378,6 +383,33 @@ fun CommunalHub( onCancel = viewModel::onEnableWorkProfileDialogCancel ) } if (viewModel is CommunalEditModeViewModel) { val showBottomSheet by viewModel.showDisclaimer.collectAsStateWithLifecycle(false) if (showBottomSheet) { val scope = rememberCoroutineScope() val sheetState = rememberModalBottomSheetState() val colors = LocalAndroidColorScheme.current ModalBottomSheet( onDismissRequest = viewModel::onDisclaimerDismissed, sheetState = sheetState, dragHandle = null, containerColor = colors.surfaceContainer, ) { DisclaimerBottomSheetContent { scope .launch { sheetState.hide() } .invokeOnCompletion { if (!sheetState.isVisible) { viewModel.onDisclaimerDismissed() } } } } } } } } Loading @@ -389,6 +421,47 @@ private fun onMotionEvent(viewModel: BaseCommunalViewModel) { viewModel.signalUserInteraction() } @Composable private fun DisclaimerBottomSheetContent(onButtonClicked: () -> Unit) { val colors = LocalAndroidColorScheme.current Column( modifier = Modifier.fillMaxWidth().padding(horizontal = 32.dp, vertical = 24.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( imageVector = Icons.Outlined.Widgets, contentDescription = null, tint = colors.primary, modifier = Modifier.size(32.dp) ) Spacer(modifier = Modifier.height(16.dp)) Text( text = stringResource(R.string.communal_widgets_disclaimer_title), style = MaterialTheme.typography.headlineMedium, color = colors.onSurface, ) Spacer(modifier = Modifier.height(16.dp)) Text( text = stringResource(R.string.communal_widgets_disclaimer_text), color = colors.onSurfaceVariant, ) Button( modifier = Modifier.padding(horizontal = 26.dp, vertical = 16.dp) .widthIn(min = 200.dp) .heightIn(min = 56.dp), onClick = { onButtonClicked() } ) { Text( stringResource(R.string.communal_widgets_disclaimer_button), style = MaterialTheme.typography.labelLarge, ) } } } /** * Observes communal content and scrolls to any added or updated live content, e.g. a new media * session is started, or a paused timer is resumed. Loading packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt +39 −96 Original line number Diff line number Diff line Loading @@ -18,8 +18,8 @@ package com.android.systemui.communal.data.repository import android.content.Context import android.content.Intent import android.content.SharedPreferences import android.content.pm.UserInfo import android.content.pm.UserInfo.FLAG_MAIN import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase Loading @@ -30,108 +30,87 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.fakeUserFileManager import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.FakeSharedPreferences import com.google.common.truth.Truth.assertThat import java.io.File import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.kotlin.spy @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class CommunalPrefsRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var tableLogBuffer: TableLogBuffer private lateinit var underTest: CommunalPrefsRepositoryImpl private val kosmos = testKosmos() private val testScope = kosmos.testScope private lateinit var userRepository: FakeUserRepository private lateinit var userFileManager: UserFileManager @Before fun setUp() { MockitoAnnotations.initMocks(this) private val userFileManager: UserFileManager = spy(kosmos.fakeUserFileManager) userRepository = kosmos.fakeUserRepository userRepository.setUserInfos(USER_INFOS) userFileManager = FakeUserFileManager( mapOf( USER_INFOS[0].id to FakeSharedPreferences(), USER_INFOS[1].id to FakeSharedPreferences() ) private val underTest: CommunalPrefsRepositoryImpl by lazy { CommunalPrefsRepositoryImpl( kosmos.testDispatcher, userFileManager, kosmos.broadcastDispatcher, logcatLogBuffer("CommunalPrefsRepositoryImplTest"), ) } @Test fun isCtaDismissedValue_byDefault_isFalse() = testScope.runTest { underTest = createCommunalPrefsRepositoryImpl(userFileManager) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER)) assertThat(isCtaDismissed).isFalse() } @Test fun isCtaDismissedValue_onSet_isTrue() = testScope.runTest { underTest = createCommunalPrefsRepositoryImpl(userFileManager) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER)) underTest.setCtaDismissedForCurrentUser() underTest.setCtaDismissed(MAIN_USER) assertThat(isCtaDismissed).isTrue() } @Test fun isCtaDismissedValue_whenSwitchUser() = fun isCtaDismissedValue_onSetForDifferentUser_isStillFalse() = testScope.runTest { underTest = createCommunalPrefsRepositoryImpl(userFileManager) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) underTest.setCtaDismissedForCurrentUser() val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER)) // dismissed true for primary user assertThat(isCtaDismissed).isTrue() // switch to secondary user userRepository.setSelectedUserInfo(USER_INFOS[1]) // dismissed is false for secondary user underTest.setCtaDismissed(SECONDARY_USER) assertThat(isCtaDismissed).isFalse() } // switch back to primary user userRepository.setSelectedUserInfo(USER_INFOS[0]) @Test fun isDisclaimerDismissed_byDefault_isFalse() = testScope.runTest { val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed(MAIN_USER)) assertThat(isDisclaimerDismissed).isFalse() } // dismissed is true for primary user assertThat(isCtaDismissed).isTrue() @Test fun isDisclaimerDismissed_onSet_isTrue() = testScope.runTest { val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed(MAIN_USER)) underTest.setDisclaimerDismissed(MAIN_USER) assertThat(isDisclaimerDismissed).isTrue() } @Test fun getSharedPreferences_whenFileRestored() = testScope.runTest { val userFileManagerSpy = Mockito.spy(userFileManager) underTest = createCommunalPrefsRepositoryImpl(userFileManagerSpy) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) userRepository.setSelectedUserInfo(USER_INFOS[0]) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER)) assertThat(isCtaDismissed).isFalse() clearInvocations(userFileManagerSpy) clearInvocations(userFileManager) // Received restore finished event. kosmos.broadcastDispatcher.sendIntentToMatchingReceiversOnly( Loading @@ -141,48 +120,12 @@ class CommunalPrefsRepositoryImplTest : SysuiTestCase() { runCurrent() // Get shared preferences from the restored file. verify(userFileManagerSpy, atLeastOnce()) .getSharedPreferences( FILE_NAME, Context.MODE_PRIVATE, userRepository.getSelectedUserInfo().id ) } private fun createCommunalPrefsRepositoryImpl(userFileManager: UserFileManager) = CommunalPrefsRepositoryImpl( testScope.backgroundScope, kosmos.testDispatcher, userRepository, userFileManager, kosmos.broadcastDispatcher, logcatLogBuffer("CommunalPrefsRepositoryImplTest"), tableLogBuffer, ) private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) : UserFileManager { override fun getFile(fileName: String, userId: Int): File { throw UnsupportedOperationException() } override fun getSharedPreferences( fileName: String, mode: Int, userId: Int ): SharedPreferences { if (fileName != FILE_NAME) { throw IllegalArgumentException("Preference files must be $FILE_NAME") } return sharedPrefs.getValue(userId) } verify(userFileManager, atLeastOnce()) .getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, MAIN_USER.id) } companion object { val USER_INFOS = listOf( UserInfo(/* id= */ 0, "zero", /* flags= */ 0), UserInfo(/* id= */ 1, "secondary", /* flags= */ 0), ) val MAIN_USER = UserInfo(0, "main", FLAG_MAIN) val SECONDARY_USER = UserInfo(1, "secondary", 0) } } packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +9 −1 Original line number Diff line number Diff line Loading @@ -488,8 +488,16 @@ class CommunalInteractorTest : SysuiTestCase() { @Test fun ctaTile_afterDismiss_doesNotShow() = testScope.runTest { // Set to main user, so we can dismiss the tile for the main user. val user = userRepository.asMainUser() userTracker.set( userInfos = listOf(user), selectedUserIndex = 0, ) runCurrent() tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) communalPrefsRepository.setCtaDismissedForCurrentUser() communalPrefsRepository.setCtaDismissed(user) val ctaTileContent by collectLastValue(underTest.ctaTileContent) Loading packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt 0 → 100644 +123 −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.communal.domain.interactor import android.content.pm.UserInfo import android.content.pm.UserInfo.FLAG_MAIN import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.settings.fakeUserTracker import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class CommunalPrefsInteractorTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val underTest by lazy { kosmos.communalPrefsInteractor } @Test fun setCtaDismissed_currentUser() = testScope.runTest { setSelectedUser(MAIN_USER) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) assertThat(isCtaDismissed).isFalse() underTest.setCtaDismissed(MAIN_USER) assertThat(isCtaDismissed).isTrue() } @Test fun setCtaDismissed_anotherUser() = testScope.runTest { setSelectedUser(MAIN_USER) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) assertThat(isCtaDismissed).isFalse() underTest.setCtaDismissed(SECONDARY_USER) assertThat(isCtaDismissed).isFalse() } @Test fun isCtaDismissed_userSwitch() = testScope.runTest { setSelectedUser(MAIN_USER) underTest.setCtaDismissed(MAIN_USER) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) assertThat(isCtaDismissed).isTrue() setSelectedUser(SECONDARY_USER) assertThat(isCtaDismissed).isFalse() } @Test fun setDisclaimerDismissed_currentUser() = testScope.runTest { setSelectedUser(MAIN_USER) val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed) assertThat(isDisclaimerDismissed).isFalse() underTest.setDisclaimerDismissed(MAIN_USER) assertThat(isDisclaimerDismissed).isTrue() } @Test fun setDisclaimerDismissed_anotherUser() = testScope.runTest { setSelectedUser(MAIN_USER) val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed) assertThat(isDisclaimerDismissed).isFalse() underTest.setDisclaimerDismissed(SECONDARY_USER) assertThat(isDisclaimerDismissed).isFalse() } @Test fun isDisclaimerDismissed_userSwitch() = testScope.runTest { setSelectedUser(MAIN_USER) underTest.setDisclaimerDismissed(MAIN_USER) val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed) assertThat(isDisclaimerDismissed).isTrue() setSelectedUser(SECONDARY_USER) assertThat(isDisclaimerDismissed).isFalse() } private suspend fun setSelectedUser(user: UserInfo) { with(kosmos.fakeUserRepository) { setUserInfos(listOf(user)) setSelectedUserInfo(user) } kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0) } private companion object { val MAIN_USER = UserInfo(0, "main", FLAG_MAIN) val SECONDARY_USER = UserInfo(1, "secondary", 0) } } packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +30 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import com.android.systemui.communal.data.repository.fakeCommunalTutorialReposit import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.communalPrefsInteractor import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.communal.domain.model.CommunalContentModel Loading @@ -48,6 +49,8 @@ import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope Loading @@ -57,6 +60,7 @@ import com.android.systemui.settings.fakeUserTracker import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat Loading Loading @@ -104,10 +108,12 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { smartspaceRepository = kosmos.fakeSmartspaceRepository mediaRepository = kosmos.fakeCommunalMediaRepository communalSceneInteractor = kosmos.communalSceneInteractor kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO)) kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0, ) kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true) whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id)) underTest = Loading @@ -120,6 +126,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { uiEventLogger, logcatLogBuffer("CommunalEditModeViewModelTest"), kosmos.testDispatcher, kosmos.communalPrefsInteractor, ) } Loading Loading @@ -312,6 +319,29 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { } } @Test fun showDisclaimer_trueAfterEditModeShowing() = testScope.runTest { val showDisclaimer by collectLastValue(underTest.showDisclaimer) assertThat(showDisclaimer).isFalse() underTest.setEditModeState(EditModeState.SHOWING) assertThat(showDisclaimer).isTrue() } @Test fun showDisclaimer_falseWhenDismissed() = testScope.runTest { underTest.setEditModeState(EditModeState.SHOWING) kosmos.fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO) val showDisclaimer by collectLastValue(underTest.showDisclaimer) assertThat(showDisclaimer).isTrue() underTest.onDisclaimerDismissed() assertThat(showDisclaimer).isFalse() } private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) const val WIDGET_PICKER_PACKAGE_NAME = "widget_picker_package_name" Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +74 −1 Original line number Diff line number Diff line Loading @@ -52,10 +52,12 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan Loading @@ -75,12 +77,15 @@ import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilledIconButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButtonColors import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State Loading Loading @@ -153,7 +158,7 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialogFactory import kotlinx.coroutines.launch @OptIn(ExperimentalComposeUiApi::class) @OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) @Composable fun CommunalHub( modifier: Modifier = Modifier, Loading Loading @@ -378,6 +383,33 @@ fun CommunalHub( onCancel = viewModel::onEnableWorkProfileDialogCancel ) } if (viewModel is CommunalEditModeViewModel) { val showBottomSheet by viewModel.showDisclaimer.collectAsStateWithLifecycle(false) if (showBottomSheet) { val scope = rememberCoroutineScope() val sheetState = rememberModalBottomSheetState() val colors = LocalAndroidColorScheme.current ModalBottomSheet( onDismissRequest = viewModel::onDisclaimerDismissed, sheetState = sheetState, dragHandle = null, containerColor = colors.surfaceContainer, ) { DisclaimerBottomSheetContent { scope .launch { sheetState.hide() } .invokeOnCompletion { if (!sheetState.isVisible) { viewModel.onDisclaimerDismissed() } } } } } } } } Loading @@ -389,6 +421,47 @@ private fun onMotionEvent(viewModel: BaseCommunalViewModel) { viewModel.signalUserInteraction() } @Composable private fun DisclaimerBottomSheetContent(onButtonClicked: () -> Unit) { val colors = LocalAndroidColorScheme.current Column( modifier = Modifier.fillMaxWidth().padding(horizontal = 32.dp, vertical = 24.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( imageVector = Icons.Outlined.Widgets, contentDescription = null, tint = colors.primary, modifier = Modifier.size(32.dp) ) Spacer(modifier = Modifier.height(16.dp)) Text( text = stringResource(R.string.communal_widgets_disclaimer_title), style = MaterialTheme.typography.headlineMedium, color = colors.onSurface, ) Spacer(modifier = Modifier.height(16.dp)) Text( text = stringResource(R.string.communal_widgets_disclaimer_text), color = colors.onSurfaceVariant, ) Button( modifier = Modifier.padding(horizontal = 26.dp, vertical = 16.dp) .widthIn(min = 200.dp) .heightIn(min = 56.dp), onClick = { onButtonClicked() } ) { Text( stringResource(R.string.communal_widgets_disclaimer_button), style = MaterialTheme.typography.labelLarge, ) } } } /** * Observes communal content and scrolls to any added or updated live content, e.g. a new media * session is started, or a paused timer is resumed. Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt +39 −96 Original line number Diff line number Diff line Loading @@ -18,8 +18,8 @@ package com.android.systemui.communal.data.repository import android.content.Context import android.content.Intent import android.content.SharedPreferences import android.content.pm.UserInfo import android.content.pm.UserInfo.FLAG_MAIN import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase Loading @@ -30,108 +30,87 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.fakeUserFileManager import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.FakeSharedPreferences import com.google.common.truth.Truth.assertThat import java.io.File import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.kotlin.spy @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class CommunalPrefsRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var tableLogBuffer: TableLogBuffer private lateinit var underTest: CommunalPrefsRepositoryImpl private val kosmos = testKosmos() private val testScope = kosmos.testScope private lateinit var userRepository: FakeUserRepository private lateinit var userFileManager: UserFileManager @Before fun setUp() { MockitoAnnotations.initMocks(this) private val userFileManager: UserFileManager = spy(kosmos.fakeUserFileManager) userRepository = kosmos.fakeUserRepository userRepository.setUserInfos(USER_INFOS) userFileManager = FakeUserFileManager( mapOf( USER_INFOS[0].id to FakeSharedPreferences(), USER_INFOS[1].id to FakeSharedPreferences() ) private val underTest: CommunalPrefsRepositoryImpl by lazy { CommunalPrefsRepositoryImpl( kosmos.testDispatcher, userFileManager, kosmos.broadcastDispatcher, logcatLogBuffer("CommunalPrefsRepositoryImplTest"), ) } @Test fun isCtaDismissedValue_byDefault_isFalse() = testScope.runTest { underTest = createCommunalPrefsRepositoryImpl(userFileManager) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER)) assertThat(isCtaDismissed).isFalse() } @Test fun isCtaDismissedValue_onSet_isTrue() = testScope.runTest { underTest = createCommunalPrefsRepositoryImpl(userFileManager) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER)) underTest.setCtaDismissedForCurrentUser() underTest.setCtaDismissed(MAIN_USER) assertThat(isCtaDismissed).isTrue() } @Test fun isCtaDismissedValue_whenSwitchUser() = fun isCtaDismissedValue_onSetForDifferentUser_isStillFalse() = testScope.runTest { underTest = createCommunalPrefsRepositoryImpl(userFileManager) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) underTest.setCtaDismissedForCurrentUser() val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER)) // dismissed true for primary user assertThat(isCtaDismissed).isTrue() // switch to secondary user userRepository.setSelectedUserInfo(USER_INFOS[1]) // dismissed is false for secondary user underTest.setCtaDismissed(SECONDARY_USER) assertThat(isCtaDismissed).isFalse() } // switch back to primary user userRepository.setSelectedUserInfo(USER_INFOS[0]) @Test fun isDisclaimerDismissed_byDefault_isFalse() = testScope.runTest { val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed(MAIN_USER)) assertThat(isDisclaimerDismissed).isFalse() } // dismissed is true for primary user assertThat(isCtaDismissed).isTrue() @Test fun isDisclaimerDismissed_onSet_isTrue() = testScope.runTest { val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed(MAIN_USER)) underTest.setDisclaimerDismissed(MAIN_USER) assertThat(isDisclaimerDismissed).isTrue() } @Test fun getSharedPreferences_whenFileRestored() = testScope.runTest { val userFileManagerSpy = Mockito.spy(userFileManager) underTest = createCommunalPrefsRepositoryImpl(userFileManagerSpy) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) userRepository.setSelectedUserInfo(USER_INFOS[0]) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER)) assertThat(isCtaDismissed).isFalse() clearInvocations(userFileManagerSpy) clearInvocations(userFileManager) // Received restore finished event. kosmos.broadcastDispatcher.sendIntentToMatchingReceiversOnly( Loading @@ -141,48 +120,12 @@ class CommunalPrefsRepositoryImplTest : SysuiTestCase() { runCurrent() // Get shared preferences from the restored file. verify(userFileManagerSpy, atLeastOnce()) .getSharedPreferences( FILE_NAME, Context.MODE_PRIVATE, userRepository.getSelectedUserInfo().id ) } private fun createCommunalPrefsRepositoryImpl(userFileManager: UserFileManager) = CommunalPrefsRepositoryImpl( testScope.backgroundScope, kosmos.testDispatcher, userRepository, userFileManager, kosmos.broadcastDispatcher, logcatLogBuffer("CommunalPrefsRepositoryImplTest"), tableLogBuffer, ) private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) : UserFileManager { override fun getFile(fileName: String, userId: Int): File { throw UnsupportedOperationException() } override fun getSharedPreferences( fileName: String, mode: Int, userId: Int ): SharedPreferences { if (fileName != FILE_NAME) { throw IllegalArgumentException("Preference files must be $FILE_NAME") } return sharedPrefs.getValue(userId) } verify(userFileManager, atLeastOnce()) .getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, MAIN_USER.id) } companion object { val USER_INFOS = listOf( UserInfo(/* id= */ 0, "zero", /* flags= */ 0), UserInfo(/* id= */ 1, "secondary", /* flags= */ 0), ) val MAIN_USER = UserInfo(0, "main", FLAG_MAIN) val SECONDARY_USER = UserInfo(1, "secondary", 0) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +9 −1 Original line number Diff line number Diff line Loading @@ -488,8 +488,16 @@ class CommunalInteractorTest : SysuiTestCase() { @Test fun ctaTile_afterDismiss_doesNotShow() = testScope.runTest { // Set to main user, so we can dismiss the tile for the main user. val user = userRepository.asMainUser() userTracker.set( userInfos = listOf(user), selectedUserIndex = 0, ) runCurrent() tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) communalPrefsRepository.setCtaDismissedForCurrentUser() communalPrefsRepository.setCtaDismissed(user) val ctaTileContent by collectLastValue(underTest.ctaTileContent) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt 0 → 100644 +123 −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.communal.domain.interactor import android.content.pm.UserInfo import android.content.pm.UserInfo.FLAG_MAIN import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.settings.fakeUserTracker import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class CommunalPrefsInteractorTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val underTest by lazy { kosmos.communalPrefsInteractor } @Test fun setCtaDismissed_currentUser() = testScope.runTest { setSelectedUser(MAIN_USER) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) assertThat(isCtaDismissed).isFalse() underTest.setCtaDismissed(MAIN_USER) assertThat(isCtaDismissed).isTrue() } @Test fun setCtaDismissed_anotherUser() = testScope.runTest { setSelectedUser(MAIN_USER) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) assertThat(isCtaDismissed).isFalse() underTest.setCtaDismissed(SECONDARY_USER) assertThat(isCtaDismissed).isFalse() } @Test fun isCtaDismissed_userSwitch() = testScope.runTest { setSelectedUser(MAIN_USER) underTest.setCtaDismissed(MAIN_USER) val isCtaDismissed by collectLastValue(underTest.isCtaDismissed) assertThat(isCtaDismissed).isTrue() setSelectedUser(SECONDARY_USER) assertThat(isCtaDismissed).isFalse() } @Test fun setDisclaimerDismissed_currentUser() = testScope.runTest { setSelectedUser(MAIN_USER) val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed) assertThat(isDisclaimerDismissed).isFalse() underTest.setDisclaimerDismissed(MAIN_USER) assertThat(isDisclaimerDismissed).isTrue() } @Test fun setDisclaimerDismissed_anotherUser() = testScope.runTest { setSelectedUser(MAIN_USER) val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed) assertThat(isDisclaimerDismissed).isFalse() underTest.setDisclaimerDismissed(SECONDARY_USER) assertThat(isDisclaimerDismissed).isFalse() } @Test fun isDisclaimerDismissed_userSwitch() = testScope.runTest { setSelectedUser(MAIN_USER) underTest.setDisclaimerDismissed(MAIN_USER) val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed) assertThat(isDisclaimerDismissed).isTrue() setSelectedUser(SECONDARY_USER) assertThat(isDisclaimerDismissed).isFalse() } private suspend fun setSelectedUser(user: UserInfo) { with(kosmos.fakeUserRepository) { setUserInfos(listOf(user)) setSelectedUserInfo(user) } kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0) } private companion object { val MAIN_USER = UserInfo(0, "main", FLAG_MAIN) val SECONDARY_USER = UserInfo(1, "secondary", 0) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +30 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import com.android.systemui.communal.data.repository.fakeCommunalTutorialReposit import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.communalPrefsInteractor import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.communal.domain.model.CommunalContentModel Loading @@ -48,6 +49,8 @@ import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope Loading @@ -57,6 +60,7 @@ import com.android.systemui.settings.fakeUserTracker import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat Loading Loading @@ -104,10 +108,12 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { smartspaceRepository = kosmos.fakeSmartspaceRepository mediaRepository = kosmos.fakeCommunalMediaRepository communalSceneInteractor = kosmos.communalSceneInteractor kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO)) kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0, ) kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true) whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id)) underTest = Loading @@ -120,6 +126,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { uiEventLogger, logcatLogBuffer("CommunalEditModeViewModelTest"), kosmos.testDispatcher, kosmos.communalPrefsInteractor, ) } Loading Loading @@ -312,6 +319,29 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { } } @Test fun showDisclaimer_trueAfterEditModeShowing() = testScope.runTest { val showDisclaimer by collectLastValue(underTest.showDisclaimer) assertThat(showDisclaimer).isFalse() underTest.setEditModeState(EditModeState.SHOWING) assertThat(showDisclaimer).isTrue() } @Test fun showDisclaimer_falseWhenDismissed() = testScope.runTest { underTest.setEditModeState(EditModeState.SHOWING) kosmos.fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO) val showDisclaimer by collectLastValue(underTest.showDisclaimer) assertThat(showDisclaimer).isTrue() underTest.onDisclaimerDismissed() assertThat(showDisclaimer).isFalse() } private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) const val WIDGET_PICKER_PACKAGE_NAME = "widget_picker_package_name" Loading