summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt103
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/dagger/NoopPosturingModule.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/PosturingRepositoryKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt23
14 files changed, 425 insertions, 70 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt
new file mode 100644
index 000000000000..0df8834618d5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2025 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.communal.posturing.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.posturing.data.repository.fake
+import com.android.systemui.communal.posturing.data.repository.posturingRepository
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PosturingInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+ private val underTest by lazy { kosmos.posturingInteractor }
+
+ @Test
+ fun testNoDebugOverride() =
+ kosmos.runTest {
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ assertThat(postured).isTrue()
+ }
+
+ @Test
+ fun testOverriddenByDebugValue() =
+ kosmos.runTest {
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ underTest.setValueForDebug(PosturedState.NotPostured)
+ posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+
+ // Repository value is overridden by debug value
+ assertThat(postured).isFalse()
+
+ underTest.setValueForDebug(PosturedState.Unknown)
+ assertThat(postured).isTrue()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt
deleted file mode 100644
index c7b7050340a0..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2025 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.communal
-
-import android.annotation.SuppressLint
-import android.app.DreamManager
-import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.commandline.Command
-import com.android.systemui.statusbar.commandline.CommandRegistry
-import java.io.PrintWriter
-import javax.inject.Inject
-
-@SysUISingleton
-class DevicePosturingCommandListener
-@Inject
-constructor(private val commandRegistry: CommandRegistry, private val dreamManager: DreamManager) :
- CoreStartable {
- private val command = DevicePosturingCommand()
-
- override fun start() {
- commandRegistry.registerCommand(COMMAND_ROOT) { command }
- }
-
- internal inner class DevicePosturingCommand : Command {
- @SuppressLint("MissingPermission")
- override fun execute(pw: PrintWriter, args: List<String>) {
- val arg = args.getOrNull(0)
- if (arg == null || arg.lowercase() == "help") {
- help(pw)
- return
- }
-
- when (arg.lowercase()) {
- "true" -> dreamManager.setDevicePostured(true)
- "false" -> dreamManager.setDevicePostured(false)
- else -> {
- pw.println("Invalid argument!")
- help(pw)
- }
- }
- }
-
- override fun help(pw: PrintWriter) {
- pw.println("Usage: $ adb shell cmd statusbar device-postured <true|false>")
- }
- }
-
- private companion object {
- const val COMMAND_ROOT = "device-postured"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
new file mode 100644
index 000000000000..47040fa4a572
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2025 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.communal
+
+import android.annotation.SuppressLint
+import android.app.DreamManager
+import android.service.dreams.Flags.allowDreamWhenPostured
+import com.android.app.tracing.coroutines.launchInTraced
+import com.android.systemui.CoreStartable
+import com.android.systemui.communal.posturing.domain.interactor.PosturingInteractor
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.dagger.CommunalTableLog
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.onEach
+
+@SysUISingleton
+class DevicePosturingListener
+@Inject
+constructor(
+ private val commandRegistry: CommandRegistry,
+ private val dreamManager: DreamManager,
+ private val interactor: PosturingInteractor,
+ @Background private val bgScope: CoroutineScope,
+ @CommunalTableLog private val tableLogBuffer: TableLogBuffer,
+) : CoreStartable {
+ private val command = DevicePosturingCommand()
+
+ @SuppressLint("MissingPermission")
+ override fun start() {
+ if (!allowDreamWhenPostured()) {
+ return
+ }
+
+ interactor.postured
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnPrefix = "",
+ columnName = "postured",
+ initialValue = false,
+ )
+ .onEach { postured -> dreamManager.setDevicePostured(postured) }
+ .launchInTraced("$TAG#collectPostured", bgScope)
+
+ commandRegistry.registerCommand(COMMAND_ROOT) { command }
+ }
+
+ internal inner class DevicePosturingCommand : Command {
+ @SuppressLint("MissingPermission")
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ val arg = args.getOrNull(0)
+ if (arg == null || arg.lowercase() == "help") {
+ help(pw)
+ return
+ }
+
+ val state =
+ when (arg.lowercase()) {
+ "true" -> PosturedState.Postured(confidence = 1f)
+ "false" -> PosturedState.NotPostured
+ "clear" -> PosturedState.Unknown
+ else -> {
+ pw.println("Invalid argument!")
+ help(pw)
+ null
+ }
+ }
+ state?.let { interactor.setValueForDebug(it) }
+ }
+
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: $ adb shell cmd statusbar device-postured <true|false|clear>")
+ }
+ }
+
+ private companion object {
+ const val COMMAND_ROOT = "device-postured"
+ const val TAG = "DevicePosturingListener"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
index e3443227685f..7358aa7b3fcd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
@@ -22,7 +22,7 @@ import com.android.systemui.communal.CommunalDreamStartable
import com.android.systemui.communal.CommunalMetricsStartable
import com.android.systemui.communal.CommunalOngoingContentStartable
import com.android.systemui.communal.CommunalSceneStartable
-import com.android.systemui.communal.DevicePosturingCommandListener
+import com.android.systemui.communal.DevicePosturingListener
import com.android.systemui.communal.log.CommunalLoggerStartable
import com.android.systemui.communal.widgets.CommunalAppWidgetHostStartable
import com.android.systemui.dagger.qualifiers.PerUser
@@ -71,6 +71,6 @@ interface CommunalStartableModule {
@Binds
@IntoMap
- @ClassKey(DevicePosturingCommandListener::class)
- fun bindDevicePosturingCommandListener(impl: DevicePosturingCommandListener): CoreStartable
+ @ClassKey(DevicePosturingListener::class)
+ fun bindDevicePosturingistener(impl: DevicePosturingListener): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/dagger/NoopPosturingModule.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/dagger/NoopPosturingModule.kt
new file mode 100644
index 000000000000..f576a2000e05
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/dagger/NoopPosturingModule.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2025 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.communal.posturing.dagger
+
+import com.android.systemui.communal.posturing.data.repository.NoOpPosturingRepository
+import com.android.systemui.communal.posturing.data.repository.PosturingRepository
+import dagger.Binds
+import dagger.Module
+
+/** Module providing a reference implementation of the posturing signal. */
+@Module
+interface NoopPosturingModule {
+ /** Binds a reference implementation of the posturing repository */
+ @Binds fun bindPosturingRepository(impl: NoOpPosturingRepository): PosturingRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt
new file mode 100644
index 000000000000..c5f357f556ca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 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.communal.posturing.data.repository
+
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+@SysUISingleton
+class NoOpPosturingRepository @Inject constructor() : PosturingRepository {
+ override val posturedState: Flow<PosturedState> =
+ MutableStateFlow(PosturedState.Unknown).asStateFlow()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt
new file mode 100644
index 000000000000..dae1a47f5be0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2025 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.communal.posturing.data.repository
+
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Repository which retrieves the postured state of the device. Posturing is defined as the device
+ * being stationary and upright.
+ */
+interface PosturingRepository {
+ /** Whether the device is currently stationary and upright. */
+ val posturedState: Flow<PosturedState>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt
new file mode 100644
index 000000000000..cd81dea9cad1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2025 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.communal.posturing.domain.interactor
+
+import com.android.systemui.communal.posturing.data.repository.PosturingRepository
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+
+@SysUISingleton
+class PosturingInteractor @Inject constructor(repository: PosturingRepository) {
+ private val debugPostured = MutableStateFlow<PosturedState>(PosturedState.Unknown)
+
+ val postured: Flow<Boolean> =
+ combine(repository.posturedState, debugPostured) { postured, debugValue ->
+ debugValue.asBoolean() ?: postured.asBoolean() ?: false
+ }
+
+ fun setValueForDebug(value: PosturedState) {
+ debugPostured.value = value
+ }
+}
+
+fun PosturedState.asBoolean(): Boolean? {
+ return when (this) {
+ is PosturedState.Postured -> true
+ PosturedState.NotPostured -> false
+ PosturedState.Unknown -> null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt
new file mode 100644
index 000000000000..431ca67315eb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2025 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.communal.posturing.shared.model
+
+sealed interface PosturedState {
+ /** Represents postured state */
+ data class Postured(val confidence: Float) : PosturedState
+
+ /** Represents unknown/uninitialized state */
+ data object Unknown : PosturedState
+
+ /** Represents state where we are not postured */
+ data object NotPostured : PosturedState
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 1125d2ccdd97..8e0beda9eff7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -72,6 +72,7 @@ import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.input.InputManager;
+import android.hardware.location.ContextHubManager;
import android.location.LocationManager;
import android.media.AudioManager;
import android.media.IAudioService;
@@ -238,6 +239,13 @@ public class FrameworkServicesModule {
@Provides
@Singleton
+ @Nullable
+ static ContextHubManager provideContextHubManager(Context context) {
+ return context.getSystemService(ContextHubManager.class);
+ }
+
+ @Provides
+ @Singleton
static DevicePolicyManager provideDevicePolicyManager(Context context) {
return context.getSystemService(DevicePolicyManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 3050cba12f09..3c68e3a09f02 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -30,6 +30,7 @@ import com.android.systemui.accessibility.SystemActionsModule;
import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
import com.android.systemui.battery.BatterySaverModule;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayOverrideModule;
+import com.android.systemui.communal.posturing.dagger.NoopPosturingModule;
import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
@@ -74,9 +75,9 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule;
import com.android.systemui.statusbar.notification.dagger.ReferenceNotificationsModule;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpModule;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpModule;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentStartableModule;
@@ -149,6 +150,7 @@ import javax.inject.Named;
RearDisplayModule.class,
RecentsModule.class,
ReferenceNotificationsModule.class,
+ NoopPosturingModule.class,
ReferenceScreenshotModule.class,
RotationLockModule.class,
RotationLockNewModule.class,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt
new file mode 100644
index 000000000000..8a597a61ee78
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 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.communal.posturing.data.repository
+
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakePosturingRepository : PosturingRepository {
+ private val _postured = MutableStateFlow<PosturedState>(PosturedState.Unknown)
+
+ override val posturedState: StateFlow<PosturedState> = _postured.asStateFlow()
+
+ fun setPosturedState(state: PosturedState) {
+ _postured.value = state
+ }
+}
+
+val PosturingRepository.fake
+ get() = this as FakePosturingRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/PosturingRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/PosturingRepositoryKosmos.kt
new file mode 100644
index 000000000000..105a3581b787
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/PosturingRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 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.communal.posturing.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.posturingRepository by Kosmos.Fixture<PosturingRepository> { FakePosturingRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt
new file mode 100644
index 000000000000..53c9c6440c69
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 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.communal.posturing.domain.interactor
+
+import com.android.systemui.communal.posturing.data.repository.posturingRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.posturingInteractor by
+ Kosmos.Fixture<PosturingInteractor> { PosturingInteractor(repository = posturingRepository) }