Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dd770191 authored by Prince's avatar Prince
Browse files

Creating home control panel dream service

Test: atest HomeControlsDreamStartableTest, HomeControlsComponentInteractorTest, HomeControlsDreamServiceTest
Flag: ACONFIG FLAG_HOME_PANEL_DREAM DEVELOPMENT
Change-Id: I97e2d551e56380c9c86d96f8f64e49e9dea7a9d3
parent a9d2c1f1
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -1083,5 +1083,25 @@
        <!-- Allow SystemUI to listen for the capabilities defined in the linked xml -->
        <property android:name="android.net.PROPERTY_SELF_CERTIFIED_CAPABILITIES"
                  android:value="@xml/self_certified_network_capabilities_both" />


        <service
            android:name="com.android.systemui.dreams.homecontrols.HomeControlsDreamService"
            android:exported="false"
            android:enabled="false"
            android:label="@string/home_controls_dream_label"
            android:description="@string/home_controls_dream_description"
            android:permission="android.permission.BIND_DREAM_SERVICE"
            android:icon="@drawable/controls_icon"
            >

            <intent-filter>
                <action android:name="android.service.dreams.DreamService" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <meta-data
                android:name="android.service.dream"
                android:resource="@xml/home_controls_dream_metadata" />
        </service>
    </application>
</manifest>
+41 −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.dreams.homecontrols

import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.panels.AuthorizedPanelsRepository
import com.android.systemui.controls.panels.selectedComponentRepository
import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock

val Kosmos.homeControlsComponentInteractor by
    Kosmos.Fixture {
        HomeControlsComponentInteractor(
            selectedComponentRepository = selectedComponentRepository,
            controlsComponent,
            authorizedPanelsRepository = authorizedPanelsRepository,
            userRepository = fakeUserRepository,
            bgScope = applicationCoroutineScope,
        )
    }

val Kosmos.controlsComponent by Kosmos.Fixture<ControlsComponent> { mock() }
val Kosmos.controlsListingController by Kosmos.Fixture<ControlsListingController> { mock() }
val Kosmos.authorizedPanelsRepository by Kosmos.Fixture<AuthorizedPanelsRepository> { mock() }
+255 −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.dreams.homecontrols

import android.content.ComponentName
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.ServiceInfo
import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.panels.AuthorizedPanelsRepository
import com.android.systemui.controls.panels.FakeSelectedComponentRepository
import com.android.systemui.controls.panels.SelectedComponentRepository
import com.android.systemui.controls.panels.selectedComponentRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
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.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@SmallTest
@RunWith(AndroidJUnit4::class)
class HomeControlsComponentInteractorTest : SysuiTestCase() {

    private val kosmos = testKosmos()

    private lateinit var controlsComponent: ControlsComponent
    private lateinit var controlsListingController: ControlsListingController
    private lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
    private lateinit var underTest: HomeControlsComponentInteractor
    private lateinit var userRepository: FakeUserRepository
    private lateinit var selectedComponentRepository: FakeSelectedComponentRepository

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        userRepository = kosmos.fakeUserRepository
        userRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER))

        controlsComponent = kosmos.controlsComponent
        authorizedPanelsRepository = kosmos.authorizedPanelsRepository
        controlsListingController = kosmos.controlsListingController
        selectedComponentRepository = kosmos.selectedComponentRepository

        selectedComponentRepository.setCurrentUserHandle(PRIMARY_USER.userHandle)
        whenever(controlsComponent.getControlsListingController())
            .thenReturn(Optional.of(controlsListingController))

        underTest =
            HomeControlsComponentInteractor(
                selectedComponentRepository,
                controlsComponent,
                authorizedPanelsRepository,
                userRepository,
                kosmos.applicationCoroutineScope,
            )
    }

    @Test
    fun testPanelComponentReturnsComponentNameForSelectedItemByUser() =
        with(kosmos) {
            testScope.runTest {
                whenever(authorizedPanelsRepository.getAuthorizedPanels())
                    .thenReturn(setOf(TEST_PACKAGE_PANEL))
                userRepository.setSelectedUserInfo(PRIMARY_USER)
                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
                val actualValue by collectLastValue(underTest.panelComponent)
                assertThat(actualValue).isNull()
                runServicesUpdate()
                assertThat(actualValue).isEqualTo(TEST_COMPONENT_PANEL)
            }
        }

    @Test
    fun testPanelComponentReturnsComponentNameAsInitialValueWithoutServiceUpdate() =
        with(kosmos) {
            testScope.runTest {
                whenever(authorizedPanelsRepository.getAuthorizedPanels())
                    .thenReturn(setOf(TEST_PACKAGE_PANEL))
                userRepository.setSelectedUserInfo(PRIMARY_USER)
                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
                whenever(controlsListingController.getCurrentServices())
                    .thenReturn(
                        listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
                    )
                val actualValue by collectLastValue(underTest.panelComponent)
                assertThat(actualValue).isEqualTo(TEST_COMPONENT_PANEL)
            }
        }

    @Test
    fun testPanelComponentReturnsNullForHomeControlsThatDoesNotSupportPanel() =
        with(kosmos) {
            testScope.runTest {
                whenever(authorizedPanelsRepository.getAuthorizedPanels())
                    .thenReturn(setOf(TEST_PACKAGE_PANEL))
                userRepository.setSelectedUserInfo(PRIMARY_USER)
                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_NON_PANEL)
                val actualValue by collectLastValue(underTest.panelComponent)
                assertThat(actualValue).isNull()
                runServicesUpdate(false)
                assertThat(actualValue).isNull()
            }
        }

    @Test
    fun testPanelComponentReturnsNullWhenPanelIsUnauthorized() =
        with(kosmos) {
            testScope.runTest {
                whenever(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf())
                userRepository.setSelectedUserInfo(PRIMARY_USER)
                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
                val actualValue by collectLastValue(underTest.panelComponent)
                assertThat(actualValue).isNull()
                runServicesUpdate()
                assertThat(actualValue).isNull()
            }
        }

    @Test
    fun testPanelComponentReturnsComponentNameForDifferentUsers() =
        with(kosmos) {
            testScope.runTest {
                whenever(authorizedPanelsRepository.getAuthorizedPanels())
                    .thenReturn(setOf(TEST_PACKAGE_PANEL))
                userRepository.setSelectedUserInfo(ANOTHER_USER)
                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_NON_PANEL)
                selectedComponentRepository.setCurrentUserHandle(ANOTHER_USER.userHandle)
                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)

                val actualValue by collectLastValue(underTest.panelComponent)
                assertThat(actualValue).isNull()
                runServicesUpdate()
                assertThat(actualValue).isEqualTo(TEST_COMPONENT_PANEL)
            }
        }

    @Test
    fun testPanelComponentReturnsNullWhenControlsComponentReturnsNullForListingController() =
        with(kosmos) {
            testScope.runTest {
                whenever(authorizedPanelsRepository.getAuthorizedPanels())
                    .thenReturn(setOf(TEST_PACKAGE_PANEL))
                whenever(controlsComponent.getControlsListingController())
                    .thenReturn(Optional.empty())
                userRepository.setSelectedUserInfo(PRIMARY_USER)
                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
                val actualValue by collectLastValue(underTest.panelComponent)
                assertThat(actualValue).isNull()
            }
        }

    private fun runServicesUpdate(hasPanelBoolean: Boolean = true) {
        val listings =
            listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = hasPanelBoolean))
        val callback = withArgCaptor { verify(controlsListingController).addCallback(capture()) }
        callback.onServicesUpdated(listings)
    }

    private fun ControlsServiceInfo(
        componentName: ComponentName,
        label: CharSequence,
        hasPanel: Boolean
    ): ControlsServiceInfo {
        val serviceInfo =
            ServiceInfo().apply {
                applicationInfo = ApplicationInfo()
                packageName = componentName.packageName
                name = componentName.className
            }
        return FakeControlsServiceInfo(context, serviceInfo, label, hasPanel)
    }

    private class FakeControlsServiceInfo(
        context: Context,
        serviceInfo: ServiceInfo,
        private val label: CharSequence,
        hasPanel: Boolean
    ) : ControlsServiceInfo(context, serviceInfo) {

        init {
            if (hasPanel) {
                panelActivity = serviceInfo.componentName
            }
        }

        override fun loadLabel(): CharSequence {
            return label
        }
    }

    companion object {
        private const val PRIMARY_USER_ID = 0
        private val PRIMARY_USER =
            UserInfo(
                /* id= */ PRIMARY_USER_ID,
                /* name= */ "primary user",
                /* flags= */ UserInfo.FLAG_PRIMARY
            )

        private const val ANOTHER_USER_ID = 1
        private val ANOTHER_USER =
            UserInfo(
                /* id= */ ANOTHER_USER_ID,
                /* name= */ "another user",
                /* flags= */ UserInfo.FLAG_PRIMARY
            )
        private const val TEST_PACKAGE = "pkg"
        private val TEST_COMPONENT = ComponentName(TEST_PACKAGE, "service")
        private const val TEST_PACKAGE_PANEL = "pkg.panel"
        private val TEST_COMPONENT_PANEL = ComponentName(TEST_PACKAGE_PANEL, "service")
        private val TEST_SELECTED_COMPONENT_PANEL =
            SelectedComponentRepository.SelectedComponent(
                TEST_PACKAGE_PANEL,
                TEST_COMPONENT_PANEL,
                true
            )
        private val TEST_SELECTED_COMPONENT_NON_PANEL =
            SelectedComponentRepository.SelectedComponent(
                TEST_PACKAGE_PANEL,
                TEST_COMPONENT_PANEL,
                false
            )
    }
}
+120 −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.dreams.homecontrols

import android.app.Activity
import android.content.ComponentName
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.settings.FakeControlsSettingsRepository
import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.FakeLogBuffer.Factory.Companion.create
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@SmallTest
@RunWith(AndroidJUnit4::class)
class HomeControlsDreamServiceTest : SysuiTestCase() {

    private val kosmos = testKosmos()

    private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
    @Mock private lateinit var taskFragmentComponentFactory: TaskFragmentComponent.Factory
    @Mock private lateinit var taskFragmentComponent: TaskFragmentComponent
    @Mock private lateinit var activity: Activity
    private val logBuffer: LogBuffer = create()

    private lateinit var underTest: HomeControlsDreamService
    private lateinit var homeControlsComponentInteractor: HomeControlsComponentInteractor
    private lateinit var fakeDreamActivityProvider: DreamActivityProvider
    private lateinit var controlsComponent: ControlsComponent
    private lateinit var controlsListingController: ControlsListingController

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
        whenever(taskFragmentComponentFactory.create(any(), any(), any(), any()))
            .thenReturn(taskFragmentComponent)

        controlsSettingsRepository = FakeControlsSettingsRepository()
        controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)

        controlsComponent = kosmos.controlsComponent
        controlsListingController = kosmos.controlsListingController

        whenever(controlsComponent.getControlsListingController())
            .thenReturn(Optional.of(controlsListingController))

        homeControlsComponentInteractor = kosmos.homeControlsComponentInteractor

        fakeDreamActivityProvider = DreamActivityProvider { activity }
        underTest =
            HomeControlsDreamService(
                controlsSettingsRepository,
                taskFragmentComponentFactory,
                homeControlsComponentInteractor,
                fakeDreamActivityProvider,
                logBuffer
            )
    }

    @Test
    fun testOnAttachedToWindowCreatesTaskFragmentComponent() {
        underTest.onAttachedToWindow()
        verify(taskFragmentComponentFactory).create(any(), any(), any(), any())
    }

    @Test
    fun testOnDetachedFromWindowDestroyTaskFragmentComponent() {
        underTest.onAttachedToWindow()
        underTest.onDetachedFromWindow()
        verify(taskFragmentComponent).destroy()
    }

    @Test
    fun testNotCreatingTaskFragmentComponentWhenActivityIsNull() {
        fakeDreamActivityProvider = DreamActivityProvider { null }
        underTest =
            HomeControlsDreamService(
                controlsSettingsRepository,
                taskFragmentComponentFactory,
                homeControlsComponentInteractor,
                fakeDreamActivityProvider,
                logBuffer
            )

        underTest.onAttachedToWindow()
        verify(taskFragmentComponentFactory, never()).create(any(), any(), any(), any())
    }

    companion object {
        private const val TEST_PACKAGE_PANEL = "pkg.panel"
        private val TEST_COMPONENT_PANEL = ComponentName(TEST_PACKAGE_PANEL, "service")
    }
}
+202 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading