Loading packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +64 −19 Original line number Diff line number Diff line Loading @@ -18,7 +18,9 @@ package com.android.systemui.communal.domain.interactor import android.app.smartspace.SmartspaceTarget import android.appwidget.AppWidgetProviderInfo import android.content.pm.UserInfo import android.os.UserHandle import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 Loading Loading @@ -51,6 +53,8 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.settings.FakeUserTracker 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 Loading Loading @@ -96,6 +100,7 @@ class CommunalInteractorTest : SysuiTestCase() { private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter private lateinit var sceneInteractor: SceneInteractor private lateinit var userTracker: FakeUserTracker private lateinit var underTest: CommunalInteractor Loading @@ -113,6 +118,7 @@ class CommunalInteractorTest : SysuiTestCase() { editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter communalPrefsRepository = kosmos.fakeCommunalPrefsRepository sceneInteractor = kosmos.sceneInteractor userTracker = kosmos.fakeUserTracker whenever(mainUser.isMain).thenReturn(true) whenever(secondaryUser.isMain).thenReturn(false) Loading Loading @@ -207,25 +213,19 @@ class CommunalInteractorTest : SysuiTestCase() { keyguardRepository.setKeyguardOccluded(false) tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) // Widgets are available. val widgets = listOf( CommunalWidgetContentModel( appWidgetId = 0, priority = 30, providerInfo = mock(), ), CommunalWidgetContentModel( appWidgetId = 1, priority = 20, providerInfo = mock(), ), CommunalWidgetContentModel( appWidgetId = 2, priority = 10, providerInfo = mock(), ), val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK) userRepository.setUserInfos(userInfos) userTracker.set( userInfos = userInfos, selectedUserIndex = 0, ) runCurrent() // Widgets available. val widget1 = createWidgetForUser(1, USER_INFO_WORK.id) val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id) val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id) val widgets = listOf(widget1, widget2, widget3) widgetRepository.setCommunalWidgets(widgets) val widgetContent by collectLastValue(underTest.widgetContent) Loading Loading @@ -752,6 +752,38 @@ class CommunalInteractorTest : SysuiTestCase() { verify(editWidgetsActivityStarter).startActivity(widgetKey) } @Test fun filterWidgets_whenUserProfileRemoved() = testScope.runTest { // Keyguard showing, and tutorial completed. keyguardRepository.setKeyguardShowing(true) keyguardRepository.setKeyguardOccluded(false) tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) // Only main user exists. val userInfos = listOf(MAIN_USER_INFO) userRepository.setUserInfos(userInfos) userTracker.set( userInfos = userInfos, selectedUserIndex = 0, ) runCurrent() val widgetContent by collectLastValue(underTest.widgetContent) // Given three widgets, and one of them is associated with pre-existing work profile. val widget1 = createWidgetForUser(1, USER_INFO_WORK.id) val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id) val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id) val widgets = listOf(widget1, widget2, widget3) widgetRepository.setCommunalWidgets(widgets) // One widget is filtered out and the remaining two link to main user id. assertThat(checkNotNull(widgetContent).size).isEqualTo(2) widgetContent!!.forEachIndexed { _, model -> assertThat(model.providerInfo.profile?.identifier).isEqualTo(MAIN_USER_INFO.id) } } private fun smartspaceTimer(id: String, timestamp: Long = 0L): SmartspaceTarget { val timer = mock(SmartspaceTarget::class.java) whenever(timer.smartspaceTargetId).thenReturn(id) Loading @@ -760,4 +792,17 @@ class CommunalInteractorTest : SysuiTestCase() { whenever(timer.creationTimeMillis).thenReturn(timestamp) return timer } private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel = mock<CommunalWidgetContentModel> { whenever(this.appWidgetId).thenReturn(appWidgetId) val providerInfo = mock<AppWidgetProviderInfo>() whenever(providerInfo.profile).thenReturn(UserHandle(userId)) whenever(this.providerInfo).thenReturn(providerInfo) } private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE) } } packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +18 −4 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ package com.android.systemui.communal.view.viewmodel import android.app.smartspace.SmartspaceTarget import android.appwidget.AppWidgetProviderInfo import android.content.pm.UserInfo import android.os.UserHandle import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -39,6 +42,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.media.controls.ui.view.MediaHost 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 Loading @@ -59,6 +63,7 @@ import org.mockito.MockitoAnnotations class CommunalEditModeViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var providerInfo: AppWidgetProviderInfo private val kosmos = testKosmos() private val testScope = kosmos.testScope Loading @@ -78,6 +83,11 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { widgetRepository = kosmos.fakeCommunalWidgetRepository smartspaceRepository = kosmos.fakeSmartspaceRepository mediaRepository = kosmos.fakeCommunalMediaRepository kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0, ) whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id)) underTest = CommunalEditModeViewModel( Loading @@ -100,12 +110,12 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { CommunalWidgetContentModel( appWidgetId = 0, priority = 30, providerInfo = mock(), providerInfo = providerInfo, ), CommunalWidgetContentModel( appWidgetId = 1, priority = 20, providerInfo = mock(), providerInfo = providerInfo, ), ) widgetRepository.setCommunalWidgets(widgets) Loading Loading @@ -156,12 +166,12 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { CommunalWidgetContentModel( appWidgetId = 0, priority = 30, providerInfo = mock(), providerInfo = providerInfo, ), CommunalWidgetContentModel( appWidgetId = 1, priority = 20, providerInfo = mock(), providerInfo = providerInfo, ), ) widgetRepository.setCommunalWidgets(widgets) Loading Loading @@ -205,4 +215,8 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { underTest.onReorderWidgetCancel() verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL) } private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) } } packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +16 −3 Original line number Diff line number Diff line Loading @@ -17,7 +17,9 @@ package com.android.systemui.communal.view.viewmodel import android.app.smartspace.SmartspaceTarget import android.appwidget.AppWidgetProviderInfo import android.content.pm.UserInfo import android.os.UserHandle import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 Loading Loading @@ -45,13 +47,13 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.settings.fakeUserTracker import com.android.systemui.shade.domain.interactor.shadeInteractor 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.user.data.repository.fakeUserRepository import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi Loading @@ -71,6 +73,7 @@ import org.mockito.MockitoAnnotations class CommunalViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost @Mock private lateinit var user: UserInfo @Mock private lateinit var providerInfo: AppWidgetProviderInfo private val kosmos = testKosmos() private val testScope = kosmos.testScope Loading Loading @@ -98,6 +101,12 @@ class CommunalViewModelTest : SysuiTestCase() { kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0, ) whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id)) underTest = CommunalViewModel( testScope, Loading Loading @@ -147,12 +156,12 @@ class CommunalViewModelTest : SysuiTestCase() { CommunalWidgetContentModel( appWidgetId = 0, priority = 30, providerInfo = mock(), providerInfo = providerInfo, ), CommunalWidgetContentModel( appWidgetId = 1, priority = 20, providerInfo = mock(), providerInfo = providerInfo, ), ) widgetRepository.setCommunalWidgets(widgets) Loading Loading @@ -225,4 +234,8 @@ class CommunalViewModelTest : SysuiTestCase() { userRepository.setUserInfos(listOf(user)) userRepository.setSelectedUserInfo(user) } private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) } } packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt +54 −1 Original line number Diff line number Diff line Loading @@ -16,7 +16,9 @@ package com.android.systemui.communal.widgets import android.appwidget.AppWidgetProviderInfo import android.content.pm.UserInfo import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_COMMUNAL_HUB Loading @@ -32,6 +34,7 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher 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.android.systemui.util.mockito.mock Loading Loading @@ -65,7 +68,7 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO)) kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO, USER_INFO_WORK)) kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true) mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) Loading @@ -76,6 +79,7 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { CommunalAppWidgetHostStartable( appWidgetHost, kosmos.communalInteractor, kosmos.fakeUserTracker, kosmos.applicationCoroutineScope, kosmos.testDispatcher, ) Loading Loading @@ -170,6 +174,46 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { } } @Test fun removeWidgetsForDeletedProfile_whenCommunalIsAvailable() = with(kosmos) { testScope.runTest { // Communal is available and work profile is configured. setCommunalAvailable(true) kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK), selectedUserIndex = 0, ) val widget1 = createWidgetForUser(1, USER_INFO_WORK.id) val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id) val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id) val widgets = listOf(widget1, widget2, widget3) fakeCommunalWidgetRepository.setCommunalWidgets(widgets) underTest.start() runCurrent() val communalWidgets by collectLastValue(fakeCommunalWidgetRepository.communalWidgets) assertThat(communalWidgets).containsExactly(widget1, widget2, widget3) // Unlock the device and remove work profile. fakeKeyguardRepository.setKeyguardShowing(false) kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0, ) runCurrent() // Communal becomes available. fakeKeyguardRepository.setKeyguardShowing(true) runCurrent() // Widget created for work profile is removed. assertThat(communalWidgets).containsExactly(widget2, widget3) } } private suspend fun setCommunalAvailable(available: Boolean) = with(kosmos) { fakeKeyguardRepository.setIsEncryptedOrLockdown(false) Loading @@ -179,7 +223,16 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, settingsValue, MAIN_USER_INFO.id) } private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel = mock<CommunalWidgetContentModel> { whenever(this.appWidgetId).thenReturn(appWidgetId) val providerInfo = mock<AppWidgetProviderInfo>() whenever(providerInfo.profile).thenReturn(UserHandle(userId)) whenever(this.providerInfo).thenReturn(providerInfo) } private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE) } } packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +24 −1 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import com.android.systemui.communal.shared.model.CommunalContentSize.FULL import com.android.systemui.communal.shared.model.CommunalContentSize.HALF import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.EditWidgetsActivityStarter Loading @@ -45,6 +46,7 @@ import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.settings.UserTracker import com.android.systemui.smartspace.data.repository.SmartspaceRepository import com.android.systemui.util.kotlin.BooleanFlowOperators.and import com.android.systemui.util.kotlin.BooleanFlowOperators.not Loading @@ -59,6 +61,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf Loading @@ -82,6 +85,7 @@ constructor( communalSettingsInteractor: CommunalSettingsInteractor, private val appWidgetHost: CommunalAppWidgetHost, private val editWidgetsActivityStarter: EditWidgetsActivityStarter, private val userTracker: UserTracker, sceneInteractor: SceneInteractor, sceneContainerFlags: SceneContainerFlags, @CommunalLog logBuffer: LogBuffer, Loading Loading @@ -262,10 +266,16 @@ constructor( fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) = widgetRepository.updateWidgetOrder(widgetIdToPriorityMap) /** All widgets present in db. */ val communalWidgets: Flow<List<CommunalWidgetContentModel>> = isCommunalAvailable.flatMapLatest { available -> if (!available) emptyFlow() else widgetRepository.communalWidgets } /** A list of widget content to be displayed in the communal hub. */ val widgetContent: Flow<List<CommunalContentModel.Widget>> = widgetRepository.communalWidgets.map { widgets -> widgets.map Widget@{ widget -> filterWidgetsByExistingUsers(widgets).map Widget@{ widget -> return@Widget CommunalContentModel.Widget( appWidgetId = widget.appWidgetId, providerInfo = widget.providerInfo, Loading Loading @@ -345,6 +355,19 @@ constructor( return@combine ongoingContent } /** * Filter and retain widgets associated with an existing user, safeguarding against displaying * stale data following user deletion. */ private fun filterWidgetsByExistingUsers( list: List<CommunalWidgetContentModel>, ): List<CommunalWidgetContentModel> { val currentUserIds = userTracker.userProfiles.map { it.id }.toSet() return list.filter { widget -> currentUserIds.contains(widget.providerInfo.profile?.identifier) } } companion object { /** * The user activity timeout which should be used when the communal hub is opened. A value Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +64 −19 Original line number Diff line number Diff line Loading @@ -18,7 +18,9 @@ package com.android.systemui.communal.domain.interactor import android.app.smartspace.SmartspaceTarget import android.appwidget.AppWidgetProviderInfo import android.content.pm.UserInfo import android.os.UserHandle import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 Loading Loading @@ -51,6 +53,8 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.settings.FakeUserTracker 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 Loading Loading @@ -96,6 +100,7 @@ class CommunalInteractorTest : SysuiTestCase() { private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter private lateinit var sceneInteractor: SceneInteractor private lateinit var userTracker: FakeUserTracker private lateinit var underTest: CommunalInteractor Loading @@ -113,6 +118,7 @@ class CommunalInteractorTest : SysuiTestCase() { editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter communalPrefsRepository = kosmos.fakeCommunalPrefsRepository sceneInteractor = kosmos.sceneInteractor userTracker = kosmos.fakeUserTracker whenever(mainUser.isMain).thenReturn(true) whenever(secondaryUser.isMain).thenReturn(false) Loading Loading @@ -207,25 +213,19 @@ class CommunalInteractorTest : SysuiTestCase() { keyguardRepository.setKeyguardOccluded(false) tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) // Widgets are available. val widgets = listOf( CommunalWidgetContentModel( appWidgetId = 0, priority = 30, providerInfo = mock(), ), CommunalWidgetContentModel( appWidgetId = 1, priority = 20, providerInfo = mock(), ), CommunalWidgetContentModel( appWidgetId = 2, priority = 10, providerInfo = mock(), ), val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK) userRepository.setUserInfos(userInfos) userTracker.set( userInfos = userInfos, selectedUserIndex = 0, ) runCurrent() // Widgets available. val widget1 = createWidgetForUser(1, USER_INFO_WORK.id) val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id) val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id) val widgets = listOf(widget1, widget2, widget3) widgetRepository.setCommunalWidgets(widgets) val widgetContent by collectLastValue(underTest.widgetContent) Loading Loading @@ -752,6 +752,38 @@ class CommunalInteractorTest : SysuiTestCase() { verify(editWidgetsActivityStarter).startActivity(widgetKey) } @Test fun filterWidgets_whenUserProfileRemoved() = testScope.runTest { // Keyguard showing, and tutorial completed. keyguardRepository.setKeyguardShowing(true) keyguardRepository.setKeyguardOccluded(false) tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) // Only main user exists. val userInfos = listOf(MAIN_USER_INFO) userRepository.setUserInfos(userInfos) userTracker.set( userInfos = userInfos, selectedUserIndex = 0, ) runCurrent() val widgetContent by collectLastValue(underTest.widgetContent) // Given three widgets, and one of them is associated with pre-existing work profile. val widget1 = createWidgetForUser(1, USER_INFO_WORK.id) val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id) val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id) val widgets = listOf(widget1, widget2, widget3) widgetRepository.setCommunalWidgets(widgets) // One widget is filtered out and the remaining two link to main user id. assertThat(checkNotNull(widgetContent).size).isEqualTo(2) widgetContent!!.forEachIndexed { _, model -> assertThat(model.providerInfo.profile?.identifier).isEqualTo(MAIN_USER_INFO.id) } } private fun smartspaceTimer(id: String, timestamp: Long = 0L): SmartspaceTarget { val timer = mock(SmartspaceTarget::class.java) whenever(timer.smartspaceTargetId).thenReturn(id) Loading @@ -760,4 +792,17 @@ class CommunalInteractorTest : SysuiTestCase() { whenever(timer.creationTimeMillis).thenReturn(timestamp) return timer } private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel = mock<CommunalWidgetContentModel> { whenever(this.appWidgetId).thenReturn(appWidgetId) val providerInfo = mock<AppWidgetProviderInfo>() whenever(providerInfo.profile).thenReturn(UserHandle(userId)) whenever(this.providerInfo).thenReturn(providerInfo) } private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +18 −4 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ package com.android.systemui.communal.view.viewmodel import android.app.smartspace.SmartspaceTarget import android.appwidget.AppWidgetProviderInfo import android.content.pm.UserInfo import android.os.UserHandle import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -39,6 +42,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.media.controls.ui.view.MediaHost 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 Loading @@ -59,6 +63,7 @@ import org.mockito.MockitoAnnotations class CommunalEditModeViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var providerInfo: AppWidgetProviderInfo private val kosmos = testKosmos() private val testScope = kosmos.testScope Loading @@ -78,6 +83,11 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { widgetRepository = kosmos.fakeCommunalWidgetRepository smartspaceRepository = kosmos.fakeSmartspaceRepository mediaRepository = kosmos.fakeCommunalMediaRepository kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0, ) whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id)) underTest = CommunalEditModeViewModel( Loading @@ -100,12 +110,12 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { CommunalWidgetContentModel( appWidgetId = 0, priority = 30, providerInfo = mock(), providerInfo = providerInfo, ), CommunalWidgetContentModel( appWidgetId = 1, priority = 20, providerInfo = mock(), providerInfo = providerInfo, ), ) widgetRepository.setCommunalWidgets(widgets) Loading Loading @@ -156,12 +166,12 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { CommunalWidgetContentModel( appWidgetId = 0, priority = 30, providerInfo = mock(), providerInfo = providerInfo, ), CommunalWidgetContentModel( appWidgetId = 1, priority = 20, providerInfo = mock(), providerInfo = providerInfo, ), ) widgetRepository.setCommunalWidgets(widgets) Loading Loading @@ -205,4 +215,8 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { underTest.onReorderWidgetCancel() verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL) } private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +16 −3 Original line number Diff line number Diff line Loading @@ -17,7 +17,9 @@ package com.android.systemui.communal.view.viewmodel import android.app.smartspace.SmartspaceTarget import android.appwidget.AppWidgetProviderInfo import android.content.pm.UserInfo import android.os.UserHandle import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 Loading Loading @@ -45,13 +47,13 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.settings.fakeUserTracker import com.android.systemui.shade.domain.interactor.shadeInteractor 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.user.data.repository.fakeUserRepository import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi Loading @@ -71,6 +73,7 @@ import org.mockito.MockitoAnnotations class CommunalViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost @Mock private lateinit var user: UserInfo @Mock private lateinit var providerInfo: AppWidgetProviderInfo private val kosmos = testKosmos() private val testScope = kosmos.testScope Loading Loading @@ -98,6 +101,12 @@ class CommunalViewModelTest : SysuiTestCase() { kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0, ) whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id)) underTest = CommunalViewModel( testScope, Loading Loading @@ -147,12 +156,12 @@ class CommunalViewModelTest : SysuiTestCase() { CommunalWidgetContentModel( appWidgetId = 0, priority = 30, providerInfo = mock(), providerInfo = providerInfo, ), CommunalWidgetContentModel( appWidgetId = 1, priority = 20, providerInfo = mock(), providerInfo = providerInfo, ), ) widgetRepository.setCommunalWidgets(widgets) Loading Loading @@ -225,4 +234,8 @@ class CommunalViewModelTest : SysuiTestCase() { userRepository.setUserInfos(listOf(user)) userRepository.setSelectedUserInfo(user) } private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt +54 −1 Original line number Diff line number Diff line Loading @@ -16,7 +16,9 @@ package com.android.systemui.communal.widgets import android.appwidget.AppWidgetProviderInfo import android.content.pm.UserInfo import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_COMMUNAL_HUB Loading @@ -32,6 +34,7 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher 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.android.systemui.util.mockito.mock Loading Loading @@ -65,7 +68,7 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO)) kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO, USER_INFO_WORK)) kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true) mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) Loading @@ -76,6 +79,7 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { CommunalAppWidgetHostStartable( appWidgetHost, kosmos.communalInteractor, kosmos.fakeUserTracker, kosmos.applicationCoroutineScope, kosmos.testDispatcher, ) Loading Loading @@ -170,6 +174,46 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { } } @Test fun removeWidgetsForDeletedProfile_whenCommunalIsAvailable() = with(kosmos) { testScope.runTest { // Communal is available and work profile is configured. setCommunalAvailable(true) kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK), selectedUserIndex = 0, ) val widget1 = createWidgetForUser(1, USER_INFO_WORK.id) val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id) val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id) val widgets = listOf(widget1, widget2, widget3) fakeCommunalWidgetRepository.setCommunalWidgets(widgets) underTest.start() runCurrent() val communalWidgets by collectLastValue(fakeCommunalWidgetRepository.communalWidgets) assertThat(communalWidgets).containsExactly(widget1, widget2, widget3) // Unlock the device and remove work profile. fakeKeyguardRepository.setKeyguardShowing(false) kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0, ) runCurrent() // Communal becomes available. fakeKeyguardRepository.setKeyguardShowing(true) runCurrent() // Widget created for work profile is removed. assertThat(communalWidgets).containsExactly(widget2, widget3) } } private suspend fun setCommunalAvailable(available: Boolean) = with(kosmos) { fakeKeyguardRepository.setIsEncryptedOrLockdown(false) Loading @@ -179,7 +223,16 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, settingsValue, MAIN_USER_INFO.id) } private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel = mock<CommunalWidgetContentModel> { whenever(this.appWidgetId).thenReturn(appWidgetId) val providerInfo = mock<AppWidgetProviderInfo>() whenever(providerInfo.profile).thenReturn(UserHandle(userId)) whenever(this.providerInfo).thenReturn(providerInfo) } private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE) } }
packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +24 −1 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import com.android.systemui.communal.shared.model.CommunalContentSize.FULL import com.android.systemui.communal.shared.model.CommunalContentSize.HALF import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.EditWidgetsActivityStarter Loading @@ -45,6 +46,7 @@ import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.settings.UserTracker import com.android.systemui.smartspace.data.repository.SmartspaceRepository import com.android.systemui.util.kotlin.BooleanFlowOperators.and import com.android.systemui.util.kotlin.BooleanFlowOperators.not Loading @@ -59,6 +61,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf Loading @@ -82,6 +85,7 @@ constructor( communalSettingsInteractor: CommunalSettingsInteractor, private val appWidgetHost: CommunalAppWidgetHost, private val editWidgetsActivityStarter: EditWidgetsActivityStarter, private val userTracker: UserTracker, sceneInteractor: SceneInteractor, sceneContainerFlags: SceneContainerFlags, @CommunalLog logBuffer: LogBuffer, Loading Loading @@ -262,10 +266,16 @@ constructor( fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) = widgetRepository.updateWidgetOrder(widgetIdToPriorityMap) /** All widgets present in db. */ val communalWidgets: Flow<List<CommunalWidgetContentModel>> = isCommunalAvailable.flatMapLatest { available -> if (!available) emptyFlow() else widgetRepository.communalWidgets } /** A list of widget content to be displayed in the communal hub. */ val widgetContent: Flow<List<CommunalContentModel.Widget>> = widgetRepository.communalWidgets.map { widgets -> widgets.map Widget@{ widget -> filterWidgetsByExistingUsers(widgets).map Widget@{ widget -> return@Widget CommunalContentModel.Widget( appWidgetId = widget.appWidgetId, providerInfo = widget.providerInfo, Loading Loading @@ -345,6 +355,19 @@ constructor( return@combine ongoingContent } /** * Filter and retain widgets associated with an existing user, safeguarding against displaying * stale data following user deletion. */ private fun filterWidgetsByExistingUsers( list: List<CommunalWidgetContentModel>, ): List<CommunalWidgetContentModel> { val currentUserIds = userTracker.userProfiles.map { it.id }.toSet() return list.filter { widget -> currentUserIds.contains(widget.providerInfo.profile?.identifier) } } companion object { /** * The user activity timeout which should be used when the communal hub is opened. A value Loading