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

Commit 48d994f5 authored by Joe Bolinger's avatar Joe Bolinger
Browse files

Replace FoldStateProvider with DeviceStateRepository.

FoldStateProvider does not provide an initial value and is not updated until the user interacts (i.e. folds) the device.

Flag: NONE
Bug: 308944800
Bug: 320763007
Test: atest LogContextInteractorImplTest
Test: statsd_testdrive 88
Change-Id: Ia4a7548fb0f919fb4e751994523dacfeab9d04a9
parent 29b4ce10
Loading
Loading
Loading
Loading
+16 −41
Original line number Original line Diff line number Diff line
@@ -19,22 +19,15 @@ package com.android.systemui.biometrics.domain.interactor
import android.hardware.biometrics.AuthenticateOptions
import android.hardware.biometrics.AuthenticateOptions
import android.hardware.biometrics.IBiometricContextListener
import android.hardware.biometrics.IBiometricContextListener
import android.util.Log
import android.util.Log
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
import com.android.systemui.unfold.updates.FoldStateProvider
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.catch
@@ -80,15 +73,11 @@ class LogContextInteractorImpl
@Inject
@Inject
constructor(
constructor(
    @Application private val applicationScope: CoroutineScope,
    @Application private val applicationScope: CoroutineScope,
    private val foldProvider: FoldStateProvider,
    deviceStateRepository: DeviceStateRepository,
    keyguardTransitionInteractor: KeyguardTransitionInteractor,
    keyguardTransitionInteractor: KeyguardTransitionInteractor,
    udfpsOverlayInteractor: UdfpsOverlayInteractor,
    udfpsOverlayInteractor: UdfpsOverlayInteractor,
) : LogContextInteractor {
) : LogContextInteractor {


    init {
        applicationScope.launch { foldProvider.start() }
    }

    override val displayState =
    override val displayState =
        keyguardTransitionInteractor.startedKeyguardTransitionStep.map {
        keyguardTransitionInteractor.startedKeyguardTransitionStep.map {
            when (it.to) {
            when (it.to) {
@@ -123,32 +112,21 @@ constructor(
            .distinctUntilChanged()
            .distinctUntilChanged()


    override val foldState: Flow<Int> =
    override val foldState: Flow<Int> =
        conflatedCallbackFlow {
        deviceStateRepository.state
                val callback =
            .map {
                    object : FoldStateProvider.FoldUpdatesListener {
                when (it) {
                        override fun onHingeAngleUpdate(angle: Float) {}
                    DeviceStateRepository.DeviceState.UNFOLDED,

                    DeviceStateRepository.DeviceState.REAR_DISPLAY,
                        override fun onFoldUpdate(@FoldStateProvider.FoldUpdate update: Int) {
                    DeviceStateRepository.DeviceState.CONCURRENT_DISPLAY ->
                            val loggedState =
                                when (update) {
                                    FOLD_UPDATE_FINISH_HALF_OPEN ->
                                        IBiometricContextListener.FoldState.HALF_OPENED
                                    FOLD_UPDATE_FINISH_FULL_OPEN ->
                        IBiometricContextListener.FoldState.FULLY_OPENED
                        IBiometricContextListener.FoldState.FULLY_OPENED
                                    FOLD_UPDATE_FINISH_CLOSED ->
                    DeviceStateRepository.DeviceState.FOLDED ->
                        IBiometricContextListener.FoldState.FULLY_CLOSED
                        IBiometricContextListener.FoldState.FULLY_CLOSED
                                    else -> null
                    DeviceStateRepository.DeviceState.HALF_FOLDED ->
                                }
                        IBiometricContextListener.FoldState.HALF_OPENED
                            if (loggedState != null) {
                    else -> IBiometricContextListener.FoldState.UNKNOWN
                                trySendWithFailureLogging(loggedState, TAG)
                            }
                        }
                }
                }

                foldProvider.addCallback(callback)
                trySendWithFailureLogging(IBiometricContextListener.FoldState.UNKNOWN, TAG)
                awaitClose { foldProvider.removeCallback(callback) }
            }
            }
            .distinctUntilChanged()
            .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1)
            .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1)


    override fun addBiometricContextListener(listener: IBiometricContextListener): Job {
    override fun addBiometricContextListener(listener: IBiometricContextListener): Job {
@@ -178,6 +156,3 @@ constructor(
        private const val TAG = "ContextRepositoryImpl"
        private const val TAG = "ContextRepositoryImpl"
    }
    }
}
}

private val WakefulnessLifecycle.isAwake: Boolean
    get() = wakefulness == WakefulnessLifecycle.WAKEFULNESS_AWAKE
+44 −54
Original line number Original line 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.biometrics.domain.interactor
package com.android.systemui.biometrics.domain.interactor


import android.hardware.biometrics.AuthenticateOptions
import android.hardware.biometrics.AuthenticateOptions
@@ -5,24 +21,19 @@ import android.hardware.biometrics.IBiometricContextListener
import android.hardware.biometrics.IBiometricContextListener.FoldState
import android.hardware.biometrics.IBiometricContextListener.FoldState
import androidx.test.filters.SmallTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.display.data.repository.fakeDeviceStateRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
import com.android.systemui.kosmos.testScope
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
import com.android.systemui.testKosmos
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Before
@@ -30,8 +41,6 @@ import org.junit.Rule
import org.junit.Test
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoJUnit


@OptIn(ExperimentalCoroutinesApi::class)
@OptIn(ExperimentalCoroutinesApi::class)
@@ -41,31 +50,20 @@ class LogContextInteractorImplTest : SysuiTestCase() {


    @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
    @JvmField @Rule var mockitoRule = MockitoJUnit.rule()


    private val testScope = TestScope()
    private val kosmos = testKosmos()

    private val testScope = kosmos.testScope
    @Mock private lateinit var foldProvider: FoldStateProvider
    private val deviceStateRepository = kosmos.fakeDeviceStateRepository
    @Mock private lateinit var authController: AuthController
    private val udfpsOverlayInteractor = kosmos.udfpsOverlayInteractor
    @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository

    private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
    private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository


    private lateinit var interactor: LogContextInteractorImpl
    private lateinit var interactor: LogContextInteractorImpl


    @Before
    @Before
    fun setup() {
    fun setup() {
        keyguardTransitionRepository = FakeKeyguardTransitionRepository()
        udfpsOverlayInteractor =
            UdfpsOverlayInteractor(
                context,
                authController,
                selectedUserInteractor,
                testScope.backgroundScope,
            )
        interactor =
        interactor =
            LogContextInteractorImpl(
            LogContextInteractorImpl(
                testScope.backgroundScope,
                testScope.backgroundScope,
                foldProvider,
                deviceStateRepository,
                KeyguardTransitionInteractorFactory.create(
                KeyguardTransitionInteractorFactory.create(
                        repository = keyguardTransitionRepository,
                        repository = keyguardTransitionRepository,
                        scope = testScope.backgroundScope,
                        scope = testScope.backgroundScope,
@@ -189,33 +187,31 @@ class LogContextInteractorImplTest : SysuiTestCase() {
    @Test
    @Test
    fun foldStateChanges() =
    fun foldStateChanges() =
        testScope.runTest {
        testScope.runTest {
            val foldState = collectLastValue(interactor.foldState)
            val foldState by collectLastValue(interactor.foldState)
            runCurrent()

            val listener = foldProvider.captureListener()
            deviceStateRepository.emit(DeviceStateRepository.DeviceState.HALF_FOLDED)
            assertThat(foldState).isEqualTo(FoldState.HALF_OPENED)


            listener.onFoldUpdate(FOLD_UPDATE_START_OPENING)
            deviceStateRepository.emit(DeviceStateRepository.DeviceState.CONCURRENT_DISPLAY)
            assertThat(foldState()).isEqualTo(FoldState.UNKNOWN)
            assertThat(foldState).isEqualTo(FoldState.FULLY_OPENED)


            listener.onFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
            deviceStateRepository.emit(DeviceStateRepository.DeviceState.UNFOLDED)
            assertThat(foldState()).isEqualTo(FoldState.HALF_OPENED)
            assertThat(foldState).isEqualTo(FoldState.FULLY_OPENED)


            listener.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
            deviceStateRepository.emit(DeviceStateRepository.DeviceState.FOLDED)
            assertThat(foldState()).isEqualTo(FoldState.FULLY_OPENED)
            assertThat(foldState).isEqualTo(FoldState.FULLY_CLOSED)


            listener.onFoldUpdate(FOLD_UPDATE_START_CLOSING)
            deviceStateRepository.emit(DeviceStateRepository.DeviceState.REAR_DISPLAY)
            assertThat(foldState()).isEqualTo(FoldState.FULLY_OPENED)
            assertThat(foldState).isEqualTo(FoldState.FULLY_OPENED)


            listener.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
            deviceStateRepository.emit(DeviceStateRepository.DeviceState.UNKNOWN)
            assertThat(foldState()).isEqualTo(FoldState.FULLY_CLOSED)
            assertThat(foldState).isEqualTo(FoldState.UNKNOWN)
        }
        }


    @Test
    @Test
    fun contextSubscriberChanges() =
    fun contextSubscriberChanges() =
        testScope.runTest {
        testScope.runTest {
            runCurrent()
            deviceStateRepository.emit(DeviceStateRepository.DeviceState.FOLDED)
            val foldListener = foldProvider.captureListener()
            foldListener.onFoldUpdate(FOLD_UPDATE_START_CLOSING)
            foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
            keyguardTransitionRepository.startTransitionTo(KeyguardState.AOD)
            keyguardTransitionRepository.startTransitionTo(KeyguardState.AOD)


            var folded: Int? = null
            var folded: Int? = null
@@ -243,8 +239,7 @@ class LogContextInteractorImplTest : SysuiTestCase() {
            assertThat(displayState).isEqualTo(AuthenticateOptions.DISPLAY_STATE_AOD)
            assertThat(displayState).isEqualTo(AuthenticateOptions.DISPLAY_STATE_AOD)
            assertThat(ignoreTouches).isFalse()
            assertThat(ignoreTouches).isFalse()


            foldListener.onFoldUpdate(FOLD_UPDATE_START_OPENING)
            deviceStateRepository.emit(DeviceStateRepository.DeviceState.HALF_FOLDED)
            foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
            keyguardTransitionRepository.startTransitionTo(KeyguardState.LOCKSCREEN)
            keyguardTransitionRepository.startTransitionTo(KeyguardState.LOCKSCREEN)
            runCurrent()
            runCurrent()


@@ -259,7 +254,7 @@ class LogContextInteractorImplTest : SysuiTestCase() {
            job.cancel()
            job.cancel()


            // stale updates should be ignored
            // stale updates should be ignored
            foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
            deviceStateRepository.emit(DeviceStateRepository.DeviceState.UNFOLDED)
            keyguardTransitionRepository.startTransitionTo(KeyguardState.AOD)
            keyguardTransitionRepository.startTransitionTo(KeyguardState.AOD)
            runCurrent()
            runCurrent()


@@ -270,8 +265,3 @@ class LogContextInteractorImplTest : SysuiTestCase() {


private suspend fun FakeKeyguardTransitionRepository.startTransitionTo(newState: KeyguardState) =
private suspend fun FakeKeyguardTransitionRepository.startTransitionTo(newState: KeyguardState) =
    sendTransitionStep(TransitionStep(to = newState, transitionState = TransitionState.STARTED))
    sendTransitionStep(TransitionStep(to = newState, transitionState = TransitionState.STARTED))

private fun FoldStateProvider.captureListener() =
    withArgCaptor<FoldStateProvider.FoldUpdatesListener> {
        verify(this@captureListener).addCallback(capture())
    }
+24 −0
Original line number Original line 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.display.data.repository

import com.android.systemui.kosmos.Kosmos

var Kosmos.deviceStateRepository: DeviceStateRepository by
    Kosmos.Fixture { fakeDeviceStateRepository }
val Kosmos.fakeDeviceStateRepository: FakeDeviceStateRepository by
    Kosmos.Fixture { FakeDeviceStateRepository() }