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