1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.systemui.dreams.homecontrols 17 18 import android.content.ComponentName 19 import android.content.Context 20 import android.content.pm.ApplicationInfo 21 import android.content.pm.ServiceInfo 22 import android.content.pm.UserInfo 23 import androidx.test.ext.junit.runners.AndroidJUnit4 24 import androidx.test.filters.SmallTest 25 import com.android.systemui.SysuiTestCase 26 import com.android.systemui.controls.ControlsServiceInfo 27 import com.android.systemui.controls.panels.SelectedComponentRepository 28 import com.android.systemui.controls.panels.authorizedPanelsRepository 29 import com.android.systemui.controls.panels.selectedComponentRepository 30 import com.android.systemui.coroutines.collectLastValue 31 import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsComponent 32 import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsListingController 33 import com.android.systemui.dreams.homecontrols.system.domain.interactor.homeControlsComponentInteractor 34 import com.android.systemui.kosmos.testScope 35 import com.android.systemui.settings.fakeUserTracker 36 import com.android.systemui.testKosmos 37 import com.android.systemui.user.data.repository.fakeUserRepository 38 import com.android.systemui.util.mockito.whenever 39 import com.android.systemui.util.mockito.withArgCaptor 40 import com.google.common.truth.Truth.assertThat 41 import java.util.Optional 42 import kotlinx.coroutines.ExperimentalCoroutinesApi 43 import kotlinx.coroutines.test.TestScope 44 import kotlinx.coroutines.test.runCurrent 45 import kotlinx.coroutines.test.runTest 46 import org.junit.Before 47 import org.junit.Test 48 import org.junit.runner.RunWith 49 import org.mockito.Mockito.verify 50 51 @OptIn(ExperimentalCoroutinesApi::class) 52 @SmallTest 53 @RunWith(AndroidJUnit4::class) 54 class HomeControlsComponentInteractorTest : SysuiTestCase() { 55 56 private val kosmos = testKosmos() 57 <lambda>null58 private val underTest by lazy { kosmos.homeControlsComponentInteractor } 59 60 @Before setUpnull61 fun setUp() = 62 with(kosmos) { 63 fakeUserRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER)) 64 whenever(controlsComponent.getControlsListingController()) 65 .thenReturn(Optional.of(controlsListingController)) 66 Unit 67 } 68 69 @Test testPanelComponentReturnsComponentNameForSelectedItemByUsernull70 fun testPanelComponentReturnsComponentNameForSelectedItemByUser() = 71 with(kosmos) { 72 testScope.runTest { 73 setActiveUser(PRIMARY_USER) 74 authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE)) 75 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL) 76 val actualValue by collectLastValue(underTest.panelComponent) 77 assertThat(actualValue).isNull() 78 runServicesUpdate() 79 assertThat(actualValue).isEqualTo(TEST_COMPONENT) 80 } 81 } 82 83 @Test testPanelComponentReturnsComponentNameAsInitialValueWithoutServiceUpdatenull84 fun testPanelComponentReturnsComponentNameAsInitialValueWithoutServiceUpdate() = 85 with(kosmos) { 86 testScope.runTest { 87 setActiveUser(PRIMARY_USER) 88 authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE)) 89 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL) 90 whenever(controlsListingController.getCurrentServices()) 91 .thenReturn( 92 listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = true)) 93 ) 94 val actualValue by collectLastValue(underTest.panelComponent) 95 assertThat(actualValue).isEqualTo(TEST_COMPONENT) 96 } 97 } 98 99 @Test testPanelComponentReturnsNullForHomeControlsThatDoesNotSupportPanelnull100 fun testPanelComponentReturnsNullForHomeControlsThatDoesNotSupportPanel() = 101 with(kosmos) { 102 testScope.runTest { 103 setActiveUser(PRIMARY_USER) 104 authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE)) 105 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_NON_PANEL) 106 val actualValue by collectLastValue(underTest.panelComponent) 107 assertThat(actualValue).isNull() 108 runServicesUpdate(false) 109 assertThat(actualValue).isNull() 110 } 111 } 112 113 @Test testPanelComponentReturnsNullWhenPanelIsUnauthorizednull114 fun testPanelComponentReturnsNullWhenPanelIsUnauthorized() = 115 with(kosmos) { 116 testScope.runTest { 117 setActiveUser(PRIMARY_USER) 118 authorizedPanelsRepository.removeAuthorizedPanels(setOf(TEST_PACKAGE)) 119 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL) 120 val actualValue by collectLastValue(underTest.panelComponent) 121 assertThat(actualValue).isNull() 122 runServicesUpdate() 123 assertThat(actualValue).isNull() 124 } 125 } 126 127 @Test testPanelComponentReturnsComponentNameForDifferentUsersnull128 fun testPanelComponentReturnsComponentNameForDifferentUsers() = 129 with(kosmos) { 130 testScope.runTest { 131 val actualValue by collectLastValue(underTest.panelComponent) 132 133 // Secondary user has non-panel selected. 134 setActiveUser(ANOTHER_USER) 135 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_NON_PANEL) 136 137 // Primary user has panel selected. 138 setActiveUser(PRIMARY_USER) 139 authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE)) 140 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL) 141 142 runServicesUpdate() 143 assertThat(actualValue).isEqualTo(TEST_COMPONENT) 144 145 // Back to secondary user, should be null. 146 setActiveUser(ANOTHER_USER) 147 runServicesUpdate() 148 assertThat(actualValue).isNull() 149 } 150 } 151 152 @Test testPanelComponentReturnsNullWhenControlsComponentReturnsNullForListingControllernull153 fun testPanelComponentReturnsNullWhenControlsComponentReturnsNullForListingController() = 154 with(kosmos) { 155 testScope.runTest { 156 authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE)) 157 whenever(controlsComponent.getControlsListingController()) 158 .thenReturn(Optional.empty()) 159 fakeUserRepository.setSelectedUserInfo(PRIMARY_USER) 160 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL) 161 val actualValue by collectLastValue(underTest.panelComponent) 162 assertThat(actualValue).isNull() 163 } 164 } 165 runServicesUpdatenull166 private fun runServicesUpdate(hasPanelBoolean: Boolean = true) { 167 val listings = 168 listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = hasPanelBoolean)) 169 val callback = withArgCaptor { 170 verify(kosmos.controlsListingController).addCallback(capture()) 171 } 172 callback.onServicesUpdated(listings) 173 } 174 setActiveUsernull175 private suspend fun TestScope.setActiveUser(user: UserInfo) { 176 kosmos.fakeUserRepository.setSelectedUserInfo(user) 177 kosmos.fakeUserTracker.set(listOf(user), 0) 178 runCurrent() 179 } 180 ControlsServiceInfonull181 private fun ControlsServiceInfo( 182 componentName: ComponentName, 183 label: CharSequence, 184 hasPanel: Boolean, 185 ): ControlsServiceInfo { 186 val serviceInfo = 187 ServiceInfo().apply { 188 applicationInfo = ApplicationInfo() 189 packageName = componentName.packageName 190 name = componentName.className 191 } 192 return FakeControlsServiceInfo(context, serviceInfo, label, hasPanel) 193 } 194 195 private class FakeControlsServiceInfo( 196 context: Context, 197 serviceInfo: ServiceInfo, 198 private val label: CharSequence, 199 hasPanel: Boolean, 200 ) : ControlsServiceInfo(context, serviceInfo) { 201 202 init { 203 if (hasPanel) { 204 panelActivity = serviceInfo.componentName 205 } 206 } 207 loadLabelnull208 override fun loadLabel(): CharSequence { 209 return label 210 } 211 } 212 213 companion object { 214 private const val PRIMARY_USER_ID = 0 215 private val PRIMARY_USER = 216 UserInfo( 217 /* id= */ PRIMARY_USER_ID, 218 /* name= */ "primary user", 219 /* flags= */ UserInfo.FLAG_PRIMARY, 220 ) 221 222 private const val ANOTHER_USER_ID = 1 223 private val ANOTHER_USER = 224 UserInfo( 225 /* id= */ ANOTHER_USER_ID, 226 /* name= */ "another user", 227 /* flags= */ UserInfo.FLAG_PRIMARY, 228 ) 229 private const val TEST_PACKAGE = "pkg" 230 private val TEST_COMPONENT = ComponentName(TEST_PACKAGE, "service") 231 private val TEST_SELECTED_COMPONENT_PANEL = 232 SelectedComponentRepository.SelectedComponent(TEST_PACKAGE, TEST_COMPONENT, true) 233 private val TEST_SELECTED_COMPONENT_NON_PANEL = 234 SelectedComponentRepository.SelectedComponent(TEST_PACKAGE, TEST_COMPONENT, false) 235 } 236 } 237