Merge "Add Mute custom quick affordance" into tm-qpr-dev

This commit is contained in:
Brad Hinegardner
2023-01-30 19:21:55 +00:00
committed by Android (Google) Code Review
10 changed files with 612 additions and 10 deletions

View File

@@ -31,6 +31,7 @@ import com.android.systemui.dreams.DreamMonitor
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.keyboard.KeyboardUI
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable
import com.android.systemui.log.SessionTracker
import com.android.systemui.media.dialog.MediaOutputSwitcherDialogUI
import com.android.systemui.media.RingtonePlayer
@@ -288,6 +289,14 @@ abstract class SystemUICoreStartableModule {
@ClassKey(StylusUsiPowerStartable::class)
abstract fun bindStylusUsiPowerStartable(sysui: StylusUsiPowerStartable): CoreStartable
/** Inject into MuteQuickAffordanceCoreStartable*/
@Binds
@IntoMap
@ClassKey(MuteQuickAffordanceCoreStartable::class)
abstract fun bindMuteQuickAffordanceCoreStartable(
sysui: MuteQuickAffordanceCoreStartable
): CoreStartable
/**Inject into DreamMonitor */
@Binds
@IntoMap

View File

@@ -29,6 +29,7 @@ object BuiltInKeyguardQuickAffordanceKeys {
const val DO_NOT_DISTURB = "do_not_disturb"
const val FLASHLIGHT = "flashlight"
const val HOME_CONTROLS = "home"
const val MUTE = "mute"
const val QR_CODE_SCANNER = "qr_code_scanner"
const val QUICK_ACCESS_WALLET = "wallet"
const val VIDEO_CAMERA = "video_camera"

View File

@@ -33,12 +33,13 @@ interface KeyguardDataQuickAffordanceModule {
@Provides
@ElementsIntoSet
fun quickAffordanceConfigs(
camera: CameraQuickAffordanceConfig,
doNotDisturb: DoNotDisturbQuickAffordanceConfig,
flashlight: FlashlightQuickAffordanceConfig,
home: HomeControlsKeyguardQuickAffordanceConfig,
mute: MuteQuickAffordanceConfig,
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
camera: CameraQuickAffordanceConfig,
videoCamera: VideoCameraQuickAffordanceConfig,
): Set<KeyguardQuickAffordanceConfig> {
return setOf(
@@ -46,6 +47,7 @@ interface KeyguardDataQuickAffordanceModule {
doNotDisturb,
flashlight,
home,
mute,
quickAccessWallet,
qrCodeScanner,
videoCamera,

View File

@@ -0,0 +1,144 @@
/*
* 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.data.quickaffordance
import android.content.Context
import android.media.AudioManager
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.RingerModeTracker
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import javax.inject.Inject
@SysUISingleton
class MuteQuickAffordanceConfig @Inject constructor(
context: Context,
private val userTracker: UserTracker,
private val userFileManager: UserFileManager,
private val ringerModeTracker: RingerModeTracker,
private val audioManager: AudioManager,
) : KeyguardQuickAffordanceConfig {
private var previousNonSilentMode: Int = DEFAULT_LAST_NON_SILENT_VALUE
override val key: String = BuiltInKeyguardQuickAffordanceKeys.MUTE
override val pickerName: String = context.getString(R.string.volume_ringer_status_silent)
override val pickerIconResourceId: Int = R.drawable.ic_notifications_silence
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
ringerModeTracker.ringerModeInternal.asFlow()
.onStart { emit(getLastNonSilentRingerMode()) }
.distinctUntilChanged()
.onEach { mode ->
// only remember last non-SILENT ringer mode
if (mode != null && mode != AudioManager.RINGER_MODE_SILENT) {
previousNonSilentMode = mode
}
}
.map { mode ->
val (activationState, contentDescriptionRes) = when {
audioManager.isVolumeFixed ->
ActivationState.NotSupported to
R.string.volume_ringer_hint_mute
mode == AudioManager.RINGER_MODE_SILENT ->
ActivationState.Active to
R.string.volume_ringer_hint_mute
else ->
ActivationState.Inactive to
R.string.volume_ringer_hint_unmute
}
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
Icon.Resource(
R.drawable.ic_notifications_silence,
ContentDescription.Resource(contentDescriptionRes),
),
activationState,
)
}
override fun onTriggered(
expandable: Expandable?
): KeyguardQuickAffordanceConfig.OnTriggeredResult {
val newRingerMode: Int
val currentRingerMode =
ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE
if (currentRingerMode == AudioManager.RINGER_MODE_SILENT) {
newRingerMode = previousNonSilentMode
} else {
previousNonSilentMode = currentRingerMode
newRingerMode = AudioManager.RINGER_MODE_SILENT
}
if (currentRingerMode != newRingerMode) {
audioManager.ringerModeInternal = newRingerMode
}
return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
if (audioManager.isVolumeFixed) {
KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
} else {
KeyguardQuickAffordanceConfig.PickerScreenState.Default()
}
/**
* Gets the last non-silent ringer mode from shared-preferences if it exists. This is
* cached by [MuteQuickAffordanceCoreStartable] while this affordance is selected
*/
private fun getLastNonSilentRingerMode(): Int =
userFileManager.getSharedPreferences(
MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME,
Context.MODE_PRIVATE,
userTracker.userId
).getInt(
LAST_NON_SILENT_RINGER_MODE_KEY,
ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE
)
private fun <T> LiveData<T>.asFlow(): Flow<T?> =
conflatedCallbackFlow {
val observer = Observer { value: T -> trySend(value) }
observeForever(observer)
send(value)
awaitClose { removeObserver(observer) }
}
companion object {
const val LAST_NON_SILENT_RINGER_MODE_KEY = "key_last_non_silent_ringer_mode"
const val MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME = "quick_affordance_mute_ringer_mode_cache"
private const val DEFAULT_LAST_NON_SILENT_VALUE = AudioManager.RINGER_MODE_NORMAL
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2023 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.data.quickaffordance
import android.content.Context
import android.media.AudioManager
import androidx.lifecycle.Observer
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.RingerModeTracker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import javax.inject.Inject
/**
* Store previous non-silent Ringer Mode into shared prefs to be used for Mute Lockscreen Shortcut
*/
@SysUISingleton
class MuteQuickAffordanceCoreStartable @Inject constructor(
private val featureFlags: FeatureFlags,
private val userTracker: UserTracker,
private val ringerModeTracker: RingerModeTracker,
private val userFileManager: UserFileManager,
private val keyguardQuickAffordanceRepository: KeyguardQuickAffordanceRepository,
@Application private val coroutineScope: CoroutineScope,
) : CoreStartable {
private val observer = Observer(this::updateLastNonSilentRingerMode)
override fun start() {
if (!featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)) return
// only listen to ringerModeInternal changes when Mute is one of the selected affordances
keyguardQuickAffordanceRepository
.selections
.map { selections ->
// determines if Mute is selected in any lockscreen shortcut position
val muteSelected: Boolean = selections.values.any { configList ->
configList.any { config ->
config.key == BuiltInKeyguardQuickAffordanceKeys.MUTE
}
}
if (muteSelected) {
ringerModeTracker.ringerModeInternal.observeForever(observer)
} else {
ringerModeTracker.ringerModeInternal.removeObserver(observer)
}
}
.launchIn(coroutineScope)
}
private fun updateLastNonSilentRingerMode(lastRingerMode: Int) {
if (AudioManager.RINGER_MODE_SILENT != lastRingerMode) {
userFileManager.getSharedPreferences(
MuteQuickAffordanceConfig.MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME,
Context.MODE_PRIVATE,
userTracker.userId
)
.edit()
.putInt(MuteQuickAffordanceConfig.LAST_NON_SILENT_RINGER_MODE_KEY, lastRingerMode)
.apply()
}
}
}

View File

@@ -146,7 +146,7 @@ constructor(
* Returns a snapshot of the [KeyguardQuickAffordanceConfig] instances of the affordances at the
* slot with the given ID. The configs are sorted in descending priority order.
*/
fun getSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
fun getCurrentSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
val selections = selectionManager.value.getSelections().getOrDefault(slotId, emptyList())
return configs.filter { selections.contains(it.key) }
}
@@ -155,7 +155,7 @@ constructor(
* Returns a snapshot of the IDs of the selected affordances, indexed by slot ID. The configs
* are sorted in descending priority order.
*/
fun getSelections(): Map<String, List<String>> {
fun getCurrentSelections(): Map<String, List<String>> {
return selectionManager.value.getSelections()
}
@@ -217,7 +217,7 @@ constructor(
private inner class Dumpster : Dumpable {
override fun dump(pw: PrintWriter, args: Array<out String>) {
val slotPickerRepresentations = getSlotPickerRepresentations()
val selectionsBySlotId = getSelections()
val selectionsBySlotId = getCurrentSelections()
pw.println("Slots & selections:")
slotPickerRepresentations.forEach { slotPickerRepresentation ->
val slotId = slotPickerRepresentation.id

View File

@@ -154,7 +154,7 @@ constructor(
val slots = repository.get().getSlotPickerRepresentations()
val slot = slots.find { it.id == slotId } ?: return false
val selections =
repository.get().getSelections().getOrDefault(slotId, emptyList()).toMutableList()
repository.get().getCurrentSelections().getOrDefault(slotId, emptyList()).toMutableList()
val alreadySelected = selections.remove(affordanceId)
if (!alreadySelected) {
while (selections.size > 0 && selections.size >= slot.maxSelectedAffordances) {
@@ -193,7 +193,7 @@ constructor(
if (affordanceId.isNullOrEmpty()) {
return if (
repository.get().getSelections().getOrDefault(slotId, emptyList()).isEmpty()
repository.get().getCurrentSelections().getOrDefault(slotId, emptyList()).isEmpty()
) {
false
} else {
@@ -203,7 +203,7 @@ constructor(
}
val selections =
repository.get().getSelections().getOrDefault(slotId, emptyList()).toMutableList()
repository.get().getCurrentSelections().getOrDefault(slotId, emptyList()).toMutableList()
return if (selections.remove(affordanceId)) {
repository
.get()
@@ -220,7 +220,7 @@ constructor(
/** Returns affordance IDs indexed by slot ID, for all known slots. */
suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
val slots = repository.get().getSlotPickerRepresentations()
val selections = repository.get().getSelections()
val selections = repository.get().getCurrentSelections()
val affordanceById =
getAffordancePickerRepresentations().associateBy { affordance -> affordance.id }
return slots.associate { slot ->

View File

@@ -0,0 +1,140 @@
/*
* 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.data.quickaffordance
import android.content.Context
import android.media.AudioManager
import androidx.lifecycle.LiveData
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.RingerModeTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
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.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class MuteQuickAffordanceConfigTest : SysuiTestCase() {
private lateinit var underTest: MuteQuickAffordanceConfig
@Mock
private lateinit var ringerModeTracker: RingerModeTracker
@Mock
private lateinit var audioManager: AudioManager
@Mock
private lateinit var userTracker: UserTracker
@Mock
private lateinit var userFileManager: UserFileManager
private lateinit var testScope: TestScope
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testScope = TestScope()
whenever(userTracker.userContext).thenReturn(context)
whenever(userFileManager.getSharedPreferences(any(), any(), any()))
.thenReturn(context.getSharedPreferences("mutequickaffordancetest", Context.MODE_PRIVATE))
underTest = MuteQuickAffordanceConfig(
context,
userTracker,
userFileManager,
ringerModeTracker,
audioManager
)
}
@Test
fun `picker state - volume fixed - not available`() = testScope.runTest {
//given
whenever(audioManager.isVolumeFixed).thenReturn(true)
//when
val result = underTest.getPickerScreenState()
//then
assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice, result)
}
@Test
fun `picker state - volume not fixed - available`() = testScope.runTest {
//given
whenever(audioManager.isVolumeFixed).thenReturn(false)
//when
val result = underTest.getPickerScreenState()
//then
assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.Default(), result)
}
@Test
fun `triggered - state was previously NORMAL - currently SILENT - move to previous state`() {
//given
val ringerModeCapture = argumentCaptor<Int>()
val ringerModeInternal = mock<LiveData<Int>>()
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
whenever(ringerModeInternal.value).thenReturn(AudioManager.RINGER_MODE_NORMAL)
underTest.onTriggered(null)
whenever(ringerModeInternal.value).thenReturn(AudioManager.RINGER_MODE_SILENT)
//when
val result = underTest.onTriggered(null)
verify(audioManager, times(2)).ringerModeInternal = ringerModeCapture.capture()
//then
assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
assertEquals(AudioManager.RINGER_MODE_NORMAL, ringerModeCapture.value)
}
@Test
fun `triggered - state is not SILENT - move to SILENT ringer`() {
//given
val ringerModeCapture = argumentCaptor<Int>()
val ringerModeInternal = mock<LiveData<Int>>()
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
whenever(ringerModeInternal.value).thenReturn(AudioManager.RINGER_MODE_NORMAL)
//when
val result = underTest.onTriggered(null)
verify(audioManager).ringerModeInternal = ringerModeCapture.capture()
//then
assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
assertEquals(AudioManager.RINGER_MODE_SILENT, ringerModeCapture.value)
}
}

View File

@@ -0,0 +1,220 @@
/*
* Copyright (C) 2023 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.data.quickaffordance
import android.content.Context
import android.media.AudioManager
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.RingerModeTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
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.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() {
@Mock
private lateinit var featureFlags: FeatureFlags
@Mock
private lateinit var userTracker: UserTracker
@Mock
private lateinit var ringerModeTracker: RingerModeTracker
@Mock
private lateinit var userFileManager: UserFileManager
@Mock
private lateinit var keyguardQuickAffordanceRepository: KeyguardQuickAffordanceRepository
private lateinit var testScope: TestScope
private lateinit var underTest: MuteQuickAffordanceCoreStartable
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)).thenReturn(true)
val config: KeyguardQuickAffordanceConfig = mock()
whenever(config.key).thenReturn(BuiltInKeyguardQuickAffordanceKeys.MUTE)
val emission = MutableStateFlow(mapOf("testQuickAffordanceKey" to listOf(config)))
whenever(keyguardQuickAffordanceRepository.selections).thenReturn(emission)
testScope = TestScope()
underTest = MuteQuickAffordanceCoreStartable(
featureFlags,
userTracker,
ringerModeTracker,
userFileManager,
keyguardQuickAffordanceRepository,
testScope,
)
}
@Test
fun `feature flag is OFF - do nothing with keyguardQuickAffordanceRepository`() = testScope.runTest {
//given
whenever(featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)).thenReturn(false)
//when
underTest.start()
//then
verifyZeroInteractions(keyguardQuickAffordanceRepository)
coroutineContext.cancelChildren()
}
@Test
fun `feature flag is ON - call to keyguardQuickAffordanceRepository`() = testScope.runTest {
//given
val ringerModeInternal = mock<MutableLiveData<Int>>()
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
//when
underTest.start()
runCurrent()
//then
verify(keyguardQuickAffordanceRepository).selections
coroutineContext.cancelChildren()
}
@Test
fun `ringer mode is changed to SILENT - do not save to shared preferences`() = testScope.runTest {
//given
val ringerModeInternal = mock<MutableLiveData<Int>>()
val observerCaptor = argumentCaptor<Observer<Int>>()
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
//when
underTest.start()
runCurrent()
verify(ringerModeInternal).observeForever(observerCaptor.capture())
observerCaptor.value.onChanged(AudioManager.RINGER_MODE_SILENT)
//then
verifyZeroInteractions(userFileManager)
coroutineContext.cancelChildren()
}
@Test
fun `ringerModeInternal changes to something not SILENT - is set in sharedpreferences`() = testScope.runTest {
//given
val newRingerMode = 99
val observerCaptor = argumentCaptor<Observer<Int>>()
val ringerModeInternal = mock<MutableLiveData<Int>>()
val sharedPrefs = context.getSharedPreferences("quick_affordance_mute_ringer_mode_cache_test", Context.MODE_PRIVATE)
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
whenever(
userFileManager.getSharedPreferences(eq("quick_affordance_mute_ringer_mode_cache"), any(), any())
).thenReturn(sharedPrefs)
//when
underTest.start()
runCurrent()
verify(ringerModeInternal).observeForever(observerCaptor.capture())
observerCaptor.value.onChanged(newRingerMode)
val result = sharedPrefs.getInt("key_last_non_silent_ringer_mode", -1)
//then
assertEquals(newRingerMode, result)
coroutineContext.cancelChildren()
}
@Test
fun `MUTE is in selections - observe ringerModeInternal`() = testScope.runTest {
//given
val ringerModeInternal = mock<MutableLiveData<Int>>()
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
//when
underTest.start()
runCurrent()
//then
verify(ringerModeInternal).observeForever(any())
coroutineContext.cancelChildren()
}
@Test
fun `MUTE is in selections 2x - observe ringerModeInternal`() = testScope.runTest {
//given
val config: KeyguardQuickAffordanceConfig = mock()
whenever(config.key).thenReturn(BuiltInKeyguardQuickAffordanceKeys.MUTE)
val emission = MutableStateFlow(mapOf("testKey" to listOf(config), "testkey2" to listOf(config)))
whenever(keyguardQuickAffordanceRepository.selections).thenReturn(emission)
val ringerModeInternal = mock<MutableLiveData<Int>>()
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
//when
underTest.start()
runCurrent()
//then
verify(ringerModeInternal).observeForever(any())
coroutineContext.cancelChildren()
}
@Test
fun `MUTE is not in selections - stop observing ringerModeInternal`() = testScope.runTest {
//given
val config: KeyguardQuickAffordanceConfig = mock()
whenever(config.key).thenReturn("notmutequickaffordance")
val emission = MutableStateFlow(mapOf("testKey" to listOf(config)))
whenever(keyguardQuickAffordanceRepository.selections).thenReturn(emission)
val ringerModeInternal = mock<MutableLiveData<Int>>()
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
//when
underTest.start()
runCurrent()
//then
verify(ringerModeInternal).removeObserver(any())
coroutineContext.cancelChildren()
}
}

View File

@@ -275,10 +275,10 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
expected: Map<String, List<KeyguardQuickAffordanceConfig>>,
) {
assertThat(observed).isEqualTo(expected)
assertThat(underTest.getSelections())
assertThat(underTest.getCurrentSelections())
.isEqualTo(expected.mapValues { (_, configs) -> configs.map { it.key } })
expected.forEach { (slotId, configs) ->
assertThat(underTest.getSelections(slotId)).isEqualTo(configs)
assertThat(underTest.getCurrentSelections(slotId)).isEqualTo(configs)
}
}