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

Commit f31938e3 authored by Lucas Silva's avatar Lucas Silva
Browse files

Add posturing noop implementation

The posturing signal is device-specific, so we add a reference noop
implementation which can be overridden on a per-device basis.

Bug: 383208131
Flag: android.service.dreams.allow_dream_when_postured
Test: atest PosturingInteractorTest
Change-Id: I18b22814d75f23a732061fa591f566f61e5a62b8
parent d1c1c260
Loading
Loading
Loading
Loading
+66 −0
Original line number Diff line number Diff line
/*
 * 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()
        }
}
+103 −0
Original line number Diff line number Diff line
@@ -18,21 +18,53 @@ 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 DevicePosturingCommandListener
class DevicePosturingListener
@Inject
constructor(private val commandRegistry: CommandRegistry, private val dreamManager: DreamManager) :
    CoreStartable {
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 }
    }

@@ -45,22 +77,27 @@ constructor(private val commandRegistry: CommandRegistry, private val dreamManag
                return
            }

            val state =
                when (arg.lowercase()) {
                "true" -> dreamManager.setDevicePostured(true)
                "false" -> dreamManager.setDevicePostured(false)
                    "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>")
            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"
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -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
}
+29 −0
Original line number Diff line number Diff line
/*
 * 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
}
+30 −0
Original line number Diff line number Diff line
/*
 * 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()
}
Loading