diff options
13 files changed, 797 insertions, 0 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyTest.kt new file mode 100644 index 000000000000..e57776f4db1b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyTest.kt @@ -0,0 +1,95 @@ +/* + * 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.service + +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.coroutines.collectLastValue +import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class HomeControlsRemoteProxyTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + + private val fakeBinder = kosmos.fakeHomeControlsRemoteBinder + + private val underTest by lazy { kosmos.homeControlsRemoteProxy } + + @Test + fun testRegistersOnlyWhileSubscribed() = + testScope.runTest { + assertThat(fakeBinder.callbacks).isEmpty() + + val job = launch { underTest.componentInfo.collect {} } + runCurrent() + assertThat(fakeBinder.callbacks).hasSize(1) + + job.cancel() + runCurrent() + assertThat(fakeBinder.callbacks).isEmpty() + } + + @Test + fun testEmitsOnCallback() = + testScope.runTest { + val componentInfo by collectLastValue(underTest.componentInfo) + assertThat(componentInfo).isNull() + + fakeBinder.notifyCallbacks(TEST_COMPONENT, allowTrivialControlsOnLockscreen = true) + assertThat(componentInfo) + .isEqualTo( + HomeControlsComponentInfo( + TEST_COMPONENT, + allowTrivialControlsOnLockscreen = true, + ) + ) + } + + @Test + fun testOnlyRegistersSingleCallbackForMultipleSubscribers() = + testScope.runTest { + assertThat(fakeBinder.callbacks).isEmpty() + + // 2 collectors + val job = launch { + launch { underTest.componentInfo.collect {} } + launch { underTest.componentInfo.collect {} } + } + runCurrent() + assertThat(fakeBinder.callbacks).hasSize(1) + job.cancel() + } + + private companion object { + val TEST_COMPONENT = ComponentName("pkg.test", "class.test") + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorTest.kt new file mode 100644 index 000000000000..400217503299 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorTest.kt @@ -0,0 +1,128 @@ +/* + * 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.service + +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.coroutines.collectLastValue +import com.android.systemui.dreams.homecontrols.dagger.HomeControlsRemoteServiceComponent +import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.android.systemui.util.service.ObservableServiceConnection +import com.android.systemui.util.service.PersistentConnectionManager +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +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.kotlin.argumentCaptor +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.stub +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class RemoteHomeControlsDataSourceDelegatorTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + + private val proxy = kosmos.homeControlsRemoteProxy + private val fakeBinder = kosmos.fakeHomeControlsRemoteBinder + + private val callbackCaptor = + argumentCaptor<ObservableServiceConnection.Callback<HomeControlsRemoteProxy>>() + + private val connectionManager = + mock<PersistentConnectionManager<HomeControlsRemoteProxy>> { + on { start() } doAnswer { simulateConnect() } + on { stop() } doAnswer { simulateDisconnect() } + } + private val serviceComponent = + mock<HomeControlsRemoteServiceComponent> { + on { connectionManager } doReturn connectionManager + } + + private val underTest by lazy { kosmos.remoteHomeControlsDataSourceDelegator } + + @Before + fun setUp() { + kosmos.homeControlsRemoteServiceFactory = + mock<HomeControlsRemoteServiceComponent.Factory>().stub { + on { create(callbackCaptor.capture()) } doReturn serviceComponent + } + } + + @Test + fun testQueriesComponentInfoFromBinder() = + testScope.runTest { + assertThat(fakeBinder.callbacks).isEmpty() + + val componentInfo by collectLastValue(underTest.componentInfo) + + assertThat(componentInfo).isNull() + assertThat(fakeBinder.callbacks).hasSize(1) + + fakeBinder.notifyCallbacks(TEST_COMPONENT, allowTrivialControlsOnLockscreen = true) + assertThat(componentInfo) + .isEqualTo( + HomeControlsComponentInfo( + TEST_COMPONENT, + allowTrivialControlsOnLockscreen = true, + ) + ) + } + + @Test + fun testOnlyConnectToServiceOnSubscription() = + testScope.runTest { + verify(connectionManager, never()).start() + + val job = launch { underTest.componentInfo.collect {} } + runCurrent() + verify(connectionManager, times(1)).start() + verify(connectionManager, never()).stop() + + job.cancel() + runCurrent() + verify(connectionManager, times(1)).start() + verify(connectionManager, times(1)).stop() + } + + private fun simulateConnect() { + callbackCaptor.lastValue.onConnected(mock(), proxy) + } + + private fun simulateDisconnect() { + callbackCaptor.lastValue.onDisconnected(mock(), 0) + } + + private companion object { + val TEST_COMPONENT = ComponentName("pkg.test", "class.test") + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsDataSourceModule.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsDataSourceModule.kt new file mode 100644 index 000000000000..3a2791fc503f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsDataSourceModule.kt @@ -0,0 +1,44 @@ +/* + * 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.dagger + +import com.android.systemui.Flags.homeControlsDreamHsum +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dreams.homecontrols.service.RemoteHomeControlsDataSourceDelegator +import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsDataSource +import com.android.systemui.dreams.homecontrols.system.LocalHomeControlsDataSourceDelegator +import dagger.Lazy +import dagger.Module +import dagger.Provides + +@Module +interface HomeControlsDataSourceModule { + companion object { + @Provides + @SysUISingleton + fun providesHomeControlsDataSource( + localSource: Lazy<LocalHomeControlsDataSourceDelegator>, + remoteSource: Lazy<RemoteHomeControlsDataSourceDelegator>, + ): HomeControlsDataSource { + return if (homeControlsDreamHsum()) { + remoteSource.get() + } else { + localSource.get() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsRemoteServiceComponent.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsRemoteServiceComponent.kt new file mode 100644 index 000000000000..500d15e56498 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsRemoteServiceComponent.kt @@ -0,0 +1,116 @@ +/* + * 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.dagger + +import android.content.Context +import android.content.Intent +import android.os.IBinder +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dreams.homecontrols.service.HomeControlsRemoteProxy +import com.android.systemui.dreams.homecontrols.shared.IHomeControlsRemoteProxy +import com.android.systemui.dreams.homecontrols.system.HomeControlsRemoteService +import com.android.systemui.util.service.ObservableServiceConnection +import com.android.systemui.util.service.Observer +import com.android.systemui.util.service.PersistentConnectionManager +import com.android.systemui.util.service.dagger.ObservableServiceModule +import dagger.BindsInstance +import dagger.Module +import dagger.Provides +import dagger.Subcomponent +import javax.inject.Named + +/** + * This component is responsible for generating the connection to the home controls remote service + * which runs in the SYSTEM_USER context and provides the data needed to run the home controls dream + * in the foreground user context. + */ +@Subcomponent( + modules = + [ + ObservableServiceModule::class, + HomeControlsRemoteServiceComponent.HomeControlsRemoteServiceModule::class, + ] +) +interface HomeControlsRemoteServiceComponent { + /** Creates a [HomeControlsRemoteServiceComponent]. */ + @Subcomponent.Factory + interface Factory { + fun create( + @BindsInstance callback: ObservableServiceConnection.Callback<HomeControlsRemoteProxy> + ): HomeControlsRemoteServiceComponent + } + + /** A [PersistentConnectionManager] pointing to the home controls remote service. */ + val connectionManager: PersistentConnectionManager<HomeControlsRemoteProxy> + + /** Scoped module providing specific components for the [ObservableServiceConnection]. */ + @Module + interface HomeControlsRemoteServiceModule { + companion object { + @Provides + @Named(ObservableServiceModule.SERVICE_CONNECTION) + fun providesConnection( + connection: ObservableServiceConnection<HomeControlsRemoteProxy>, + callback: ObservableServiceConnection.Callback<HomeControlsRemoteProxy>, + ): ObservableServiceConnection<HomeControlsRemoteProxy> { + connection.addCallback(callback) + return connection + } + + /** Provides the wrapper around the home controls remote binder */ + @Provides + fun providesTransformer( + factory: HomeControlsRemoteProxy.Factory + ): ObservableServiceConnection.ServiceTransformer<HomeControlsRemoteProxy> { + return ObservableServiceConnection.ServiceTransformer { service: IBinder -> + factory.create(IHomeControlsRemoteProxy.Stub.asInterface(service)) + } + } + + /** Provides the intent to connect to [HomeControlsRemoteService] */ + @Provides + fun providesIntent(@Application context: Context): Intent { + return Intent(context, HomeControlsRemoteService::class.java) + } + + /** Provides no-op [Observer] since the remote service is in the same package */ + @Provides + @Named(ObservableServiceModule.OBSERVER) + fun providesObserver(): Observer { + return object : Observer { + override fun addCallback(callback: Observer.Callback?) { + // no-op, do nothing + } + + override fun removeCallback(callback: Observer.Callback?) { + // no-op, do nothing + } + } + } + + /** + * Provides a name that will be used by [PersistentConnectionManager] when logging + * state. + */ + @Provides + @Named(ObservableServiceModule.DUMPSYS_NAME) + fun providesDumpsysName(): String { + return "HomeControlsRemoteService" + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxy.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxy.kt new file mode 100644 index 000000000000..2bcfea8c1179 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxy.kt @@ -0,0 +1,81 @@ +/* + * 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.service + +import android.content.ComponentName +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dreams.homecontrols.shared.IHomeControlsRemoteProxy +import com.android.systemui.dreams.homecontrols.shared.IOnControlsSettingsChangeListener +import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo +import com.android.systemui.dump.DumpManager +import com.android.systemui.util.kotlin.FlowDumperImpl +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.stateIn + +/** Class to wrap [IHomeControlsRemoteProxy], which exposes the current user's home controls info */ +class HomeControlsRemoteProxy +@AssistedInject +constructor( + @Background bgScope: CoroutineScope, + dumpManager: DumpManager, + @Assisted private val proxy: IHomeControlsRemoteProxy, +) : FlowDumperImpl(dumpManager) { + + private companion object { + const val TAG = "HomeControlsRemoteProxy" + } + + val componentInfo: Flow<HomeControlsComponentInfo> = + conflatedCallbackFlow { + val listener = + object : IOnControlsSettingsChangeListener.Stub() { + override fun onControlsSettingsChanged( + panelComponent: ComponentName?, + allowTrivialControlsOnLockscreen: Boolean, + ) { + trySendWithFailureLogging( + HomeControlsComponentInfo( + panelComponent, + allowTrivialControlsOnLockscreen, + ), + TAG, + ) + } + } + proxy.registerListenerForCurrentUser(listener) + awaitClose { proxy.unregisterListenerForCurrentUser(listener) } + } + .distinctUntilChanged() + .stateIn(bgScope, SharingStarted.WhileSubscribed(), null) + .dumpValue("componentInfo") + .filterNotNull() + + @AssistedFactory + interface Factory { + fun create(proxy: IHomeControlsRemoteProxy): HomeControlsRemoteProxy + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegator.kt new file mode 100644 index 000000000000..b14903d7885f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegator.kt @@ -0,0 +1,111 @@ +/* + * 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.service + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dreams.DreamLogger +import com.android.systemui.dreams.homecontrols.dagger.HomeControlsRemoteServiceComponent +import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo +import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsDataSource +import com.android.systemui.dump.DumpManager +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.dagger.DreamLog +import com.android.systemui.util.kotlin.FlowDumperImpl +import com.android.systemui.util.service.ObservableServiceConnection +import com.android.systemui.util.service.PersistentConnectionManager +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.dropWhile +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach + +/** + * Queries a remote service for [HomeControlsComponentInfo] necessary to show the home controls + * dream. + */ +@OptIn(ExperimentalCoroutinesApi::class) +@SysUISingleton +class RemoteHomeControlsDataSourceDelegator +@Inject +constructor( + @Background bgScope: CoroutineScope, + serviceFactory: HomeControlsRemoteServiceComponent.Factory, + @DreamLog logBuffer: LogBuffer, + dumpManager: DumpManager, +) : HomeControlsDataSource, FlowDumperImpl(dumpManager) { + private val logger = DreamLogger(logBuffer, TAG) + + private val connectionManager: PersistentConnectionManager<HomeControlsRemoteProxy> by lazy { + serviceFactory.create(callback).connectionManager + } + + private val proxyState = + MutableStateFlow<HomeControlsRemoteProxy?>(null) + .apply { + subscriptionCount + .map { it > 0 } + .dropWhile { !it } + .distinctUntilChanged() + .onEach { active -> + logger.d({ "Remote service connection active: $bool1" }) { bool1 = active } + if (active) { + connectionManager.start() + } else { + connectionManager.stop() + } + } + .launchIn(bgScope) + } + .dumpValue("proxyState") + + private val callback: ObservableServiceConnection.Callback<HomeControlsRemoteProxy> = + object : ObservableServiceConnection.Callback<HomeControlsRemoteProxy> { + override fun onConnected( + connection: ObservableServiceConnection<HomeControlsRemoteProxy>?, + proxy: HomeControlsRemoteProxy, + ) { + logger.d("Service connected") + proxyState.value = proxy + } + + override fun onDisconnected( + connection: ObservableServiceConnection<HomeControlsRemoteProxy>?, + reason: Int, + ) { + logger.d({ "Service disconnected with reason $int1" }) { int1 = reason } + proxyState.value = null + } + } + + override val componentInfo: Flow<HomeControlsComponentInfo> = + proxyState + .filterNotNull() + .flatMapLatest { proxy: HomeControlsRemoteProxy -> proxy.componentInfo } + .dumpWhileCollecting("componentInfo") + + private companion object { + const val TAG = "HomeControlsRemoteDataSourceDelegator" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsComponentInfo.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsComponentInfo.kt new file mode 100644 index 000000000000..b9e5080e92a9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsComponentInfo.kt @@ -0,0 +1,24 @@ +/* + * 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.shared.model + +import android.content.ComponentName + +data class HomeControlsComponentInfo( + val componentName: ComponentName?, + val allowTrivialControlsOnLockscreen: Boolean, +) diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsDataSource.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsDataSource.kt new file mode 100644 index 000000000000..8187c5412fa4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsDataSource.kt @@ -0,0 +1,24 @@ +/* + * 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.shared.model + +import kotlinx.coroutines.flow.Flow + +/** Source of data for home controls dream to get the necessary information it needs. */ +interface HomeControlsDataSource { + val componentInfo: Flow<HomeControlsComponentInfo> +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/LocalHomeControlsDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/LocalHomeControlsDataSourceDelegator.kt new file mode 100644 index 000000000000..ca255fda9e89 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/LocalHomeControlsDataSourceDelegator.kt @@ -0,0 +1,46 @@ +/* + * 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.system + +import com.android.systemui.controls.settings.ControlsSettingsRepository +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo +import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsDataSource +import com.android.systemui.dreams.homecontrols.system.domain.interactor.HomeControlsComponentInteractor +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +/** + * Queries local data sources for the [HomeControlsComponentInfo] necessary to show the home + * controls dream. + */ +@SysUISingleton +class LocalHomeControlsDataSourceDelegator +@Inject +constructor( + homeControlsComponentInteractor: HomeControlsComponentInteractor, + controlsSettingsRepository: ControlsSettingsRepository, +) : HomeControlsDataSource { + override val componentInfo: Flow<HomeControlsComponentInfo> = + combine( + homeControlsComponentInteractor.panelComponent, + controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen, + ) { panelComponent, allowActionOnTrivialControlsInLockscreen -> + HomeControlsComponentInfo(panelComponent, allowActionOnTrivialControlsInLockscreen) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyKosmos.kt new file mode 100644 index 000000000000..58ebbf4c2d9e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyKosmos.kt @@ -0,0 +1,33 @@ +/* + * 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.service + +import com.android.systemui.dump.dumpManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope + +val Kosmos.homeControlsRemoteProxy by + Kosmos.Fixture { + HomeControlsRemoteProxy( + bgScope = applicationCoroutineScope, + proxy = fakeHomeControlsRemoteBinder, + dumpManager = dumpManager, + ) + } + +val Kosmos.fakeHomeControlsRemoteBinder by + Kosmos.Fixture<FakeIHomeControlsRemoteProxyBinder> { FakeIHomeControlsRemoteProxyBinder() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorKosmos.kt new file mode 100644 index 000000000000..c85c8884e6f3 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorKosmos.kt @@ -0,0 +1,37 @@ +/* + * 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.service + +import com.android.systemui.dreams.homecontrols.dagger.HomeControlsRemoteServiceComponent +import com.android.systemui.dump.dumpManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.log.logcatLogBuffer +import org.mockito.kotlin.mock + +val Kosmos.remoteHomeControlsDataSourceDelegator by + Kosmos.Fixture { + RemoteHomeControlsDataSourceDelegator( + bgScope = applicationCoroutineScope, + serviceFactory = homeControlsRemoteServiceFactory, + logBuffer = logcatLogBuffer("HomeControlsDreamInteractor"), + dumpManager = dumpManager, + ) + } + +var Kosmos.homeControlsRemoteServiceFactory by + Kosmos.Fixture<HomeControlsRemoteServiceComponent.Factory> { mock() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/shared/model/FakeHomeControlsDataSource.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/shared/model/FakeHomeControlsDataSource.kt new file mode 100644 index 000000000000..f8ea022be0d8 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/shared/model/FakeHomeControlsDataSource.kt @@ -0,0 +1,33 @@ +/* + * 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.shared.model + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filterNotNull + +class FakeHomeControlsDataSource : HomeControlsDataSource { + + private val _componentInfo = MutableStateFlow<HomeControlsComponentInfo?>(null) + + override val componentInfo: Flow<HomeControlsComponentInfo> + get() = _componentInfo.filterNotNull() + + fun setComponentInfo(info: HomeControlsComponentInfo) { + _componentInfo.value = info + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsDataSourceKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsDataSourceKosmos.kt new file mode 100644 index 000000000000..942216b6a6cc --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsDataSourceKosmos.kt @@ -0,0 +1,25 @@ +/* + * 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.shared.model + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.homeControlsDataSource by + Kosmos.Fixture<HomeControlsDataSource> { fakeHomeControlsDataSource } + +val Kosmos.fakeHomeControlsDataSource by + Kosmos.Fixture<FakeHomeControlsDataSource> { FakeHomeControlsDataSource() } |