From 32c40738137afb0f0e2a4988a9d3fcf8b52187d3 Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Wed, 16 Nov 2022 11:18:32 -0800 Subject: [PATCH] [Bouncer] Revert from shared flow... back to a state flow with a timestamp. It seems like shared flows replay the value every time and there isn't a way to propagate one off events. I am simulating one off events by using a state flow with a unique timestamp to ensure the event emits. Test: Added unit tests Test: fail face unlock until message appears. Unlock phone and restart screen. Observe that "Enter your pin" is the initial message. Fixes: 258775372 Change-Id: I6c9fb9a2c79aed9300e9b102a45c9d4c0456536e --- .../repository/KeyguardBouncerRepository.kt | 13 +--- .../interactor/PrimaryBouncerInteractor.kt | 5 ++ .../ui/binder/KeyguardBouncerViewBinder.kt | 2 +- .../ui/viewmodel/KeyguardBouncerViewModel.kt | 5 ++ .../PrimaryBouncerInteractorTest.kt | 11 ++- .../viewmodel/KeyguardBouncerViewModelTest.kt | 68 +++++++++++++++++++ 6 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt index 9a90fe7e60bd0..783f752cbd205 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt @@ -23,10 +23,7 @@ import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel import com.android.systemui.statusbar.phone.KeyguardBouncer import javax.inject.Inject -import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow /** Encapsulates app state for the lock screen primary and alternate bouncer. */ @@ -71,12 +68,8 @@ constructor( private val _keyguardAuthenticated = MutableStateFlow(null) /** Determines if user is already unlocked */ val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow() - private val _showMessage = - MutableSharedFlow( - replay = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST - ) - val showMessage = _showMessage.asSharedFlow() + private val _showMessage = MutableStateFlow(null) + val showMessage = _showMessage.asStateFlow() private val _resourceUpdateRequests = MutableStateFlow(false) val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow() val bouncerPromptReason: Int @@ -125,7 +118,7 @@ constructor( } fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) { - _showMessage.tryEmit(bouncerShowMessageModel) + _showMessage.value = bouncerShowMessageModel } fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt index 910cdf2df5a4b..3b31dcfc43495 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt @@ -271,6 +271,11 @@ constructor( repository.setKeyguardAuthenticated(null) } + /** Notifies that the message was shown. */ + fun onMessageShown() { + repository.setShowMessage(null) + } + /** Notify that view visibility has changed. */ fun notifyBouncerVisibilityHasChanged(visibility: Int) { primaryBouncerCallbackInteractor.dispatchVisibilityChanged(visibility) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt index 7739a456fcb7a..3c927ee08494a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt @@ -32,7 +32,6 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE import kotlinx.coroutines.awaitCancellation -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @@ -182,6 +181,7 @@ object KeyguardBouncerViewBinder { launch { viewModel.bouncerShowMessage.collect { hostViewController.showMessage(it.message, it.colorStateList) + viewModel.onMessageShown() } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt index 526ae741793c4..503c8ba2ca43c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt @@ -86,6 +86,11 @@ constructor( interactor.notifyKeyguardAuthenticatedHandled() } + /** Notifies that the message was shown. */ + fun onMessageShown() { + interactor.onMessageShown() + } + /** Observe whether back button is enabled. */ fun observeOnIsBackButtonEnabled(systemUiVisibility: () -> Int): Flow { return interactor.isBackButtonEnabled.map { enabled -> diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt index 3269f5a913ae2..559f183717df9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt @@ -43,6 +43,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Answers +import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.never @@ -170,8 +171,10 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Test fun testShowMessage() { + val argCaptor = ArgumentCaptor.forClass(BouncerShowMessageModel::class.java) mPrimaryBouncerInteractor.showMessage("abc", null) - verify(repository).setShowMessage(BouncerShowMessageModel("abc", null)) + verify(repository).setShowMessage(argCaptor.capture()) + assertThat(argCaptor.value.message).isEqualTo("abc") } @Test @@ -194,6 +197,12 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { verify(repository).setKeyguardAuthenticated(true) } + @Test + fun testNotifyShowedMessage() { + mPrimaryBouncerInteractor.onMessageShown() + verify(repository).setShowMessage(null) + } + @Test fun testOnScreenTurnedOff() { mPrimaryBouncerInteractor.onScreenTurnedOff() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt new file mode 100644 index 0000000000000..37271346a51f1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 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.keyguard.ui.viewmodel + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.data.BouncerView +import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class KeyguardBouncerViewModelTest : SysuiTestCase() { + lateinit var underTest: KeyguardBouncerViewModel + @Mock lateinit var bouncerView: BouncerView + @Mock lateinit var bouncerInteractor: PrimaryBouncerInteractor + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor) + } + + @Test + fun setMessage() = + runBlocking(Dispatchers.Main.immediate) { + val flow = MutableStateFlow(null) + var message: BouncerShowMessageModel? = null + Mockito.`when`(bouncerInteractor.showMessage) + .thenReturn(flow as Flow) + // Reinitialize the view model. + underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor) + + flow.value = BouncerShowMessageModel(message = "abc", colorStateList = null) + + val job = underTest.bouncerShowMessage.onEach { message = it }.launchIn(this) + assertThat(message?.message).isEqualTo("abc") + job.cancel() + } +}