Merge "Adjust Mute Quick Affordance to not make binder calls on main thread" into tm-qpr-dev

This commit is contained in:
Brad Hinegardner
2023-02-01 20:29:45 +00:00
committed by Android (Google) Code Review
4 changed files with 88 additions and 55 deletions

View File

@@ -27,16 +27,24 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
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.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
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.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
@SysUISingleton
@@ -46,6 +54,9 @@ class MuteQuickAffordanceConfig @Inject constructor(
private val userFileManager: UserFileManager,
private val ringerModeTracker: RingerModeTracker,
private val audioManager: AudioManager,
@Application private val coroutineScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : KeyguardQuickAffordanceConfig {
private var previousNonSilentMode: Int = DEFAULT_LAST_NON_SILENT_VALUE
@@ -58,7 +69,7 @@ class MuteQuickAffordanceConfig @Inject constructor(
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
ringerModeTracker.ringerModeInternal.asFlow()
.onStart { emit(getLastNonSilentRingerMode()) }
.onStart { getLastNonSilentRingerMode() }
.distinctUntilChanged()
.onEach { mode ->
// only remember last non-SILENT ringer mode
@@ -87,54 +98,60 @@ class MuteQuickAffordanceConfig @Inject constructor(
activationState,
)
}
.flowOn(backgroundDispatcher)
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
}
coroutineScope.launch(backgroundDispatcher) {
val newRingerMode: Int
val currentRingerMode = audioManager.ringerModeInternal
if (currentRingerMode == AudioManager.RINGER_MODE_SILENT) {
newRingerMode = previousNonSilentMode
} else {
previousNonSilentMode = currentRingerMode
newRingerMode = AudioManager.RINGER_MODE_SILENT
}
if (currentRingerMode != newRingerMode) {
audioManager.ringerModeInternal = newRingerMode
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()
withContext(backgroundDispatcher) {
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 suspend fun getLastNonSilentRingerMode(): Int =
withContext(backgroundDispatcher) {
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) }
}
conflatedCallbackFlow {
val observer = Observer { value: T -> trySend(value) }
observeForever(observer)
send(value)
awaitClose { removeObserver(observer) }
}.flowOn(mainDispatcher)
companion object {
const val LAST_NON_SILENT_RINGER_MODE_KEY = "key_last_non_silent_ringer_mode"

View File

@@ -23,15 +23,18 @@ 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.dagger.qualifiers.Background
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.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
/**
@@ -45,6 +48,7 @@ class MuteQuickAffordanceCoreStartable @Inject constructor(
private val userFileManager: UserFileManager,
private val keyguardQuickAffordanceRepository: KeyguardQuickAffordanceRepository,
@Application private val coroutineScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : CoreStartable {
private val observer = Observer(this::updateLastNonSilentRingerMode)
@@ -72,15 +76,17 @@ class MuteQuickAffordanceCoreStartable @Inject constructor(
}
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()
coroutineScope.launch(backgroundDispatcher) {
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

@@ -19,7 +19,6 @@ 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
@@ -27,10 +26,12 @@ 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.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -57,13 +58,15 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
@Mock
private lateinit var userFileManager: UserFileManager
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testScope = TestScope()
testDispatcher = StandardTestDispatcher()
testScope = TestScope(testDispatcher)
whenever(userTracker.userContext).thenReturn(context)
whenever(userFileManager.getSharedPreferences(any(), any(), any()))
@@ -74,7 +77,10 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
userTracker,
userFileManager,
ringerModeTracker,
audioManager
audioManager,
testScope.backgroundScope,
testDispatcher,
testDispatcher,
)
}
@@ -103,17 +109,16 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
}
@Test
fun `triggered - state was previously NORMAL - currently SILENT - move to previous state`() {
fun `triggered - state was previously NORMAL - currently SILENT - move to previous state`() = testScope.runTest {
//given
val ringerModeCapture = argumentCaptor<Int>()
val ringerModeInternal = mock<LiveData<Int>>()
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
whenever(ringerModeInternal.value).thenReturn(AudioManager.RINGER_MODE_NORMAL)
whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
underTest.onTriggered(null)
whenever(ringerModeInternal.value).thenReturn(AudioManager.RINGER_MODE_SILENT)
whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
//when
val result = underTest.onTriggered(null)
runCurrent()
verify(audioManager, times(2)).ringerModeInternal = ringerModeCapture.capture()
//then
@@ -122,15 +127,14 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
}
@Test
fun `triggered - state is not SILENT - move to SILENT ringer`() {
fun `triggered - state is not SILENT - move to SILENT ringer`() = testScope.runTest {
//given
val ringerModeCapture = argumentCaptor<Int>()
val ringerModeInternal = mock<LiveData<Int>>()
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
whenever(ringerModeInternal.value).thenReturn(AudioManager.RINGER_MODE_NORMAL)
whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
//when
val result = underTest.onTriggered(null)
runCurrent()
verify(audioManager).ringerModeInternal = ringerModeCapture.capture()
//then

View File

@@ -37,6 +37,8 @@ import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -67,6 +69,7 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() {
@Mock
private lateinit var keyguardQuickAffordanceRepository: KeyguardQuickAffordanceRepository
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
private lateinit var underTest: MuteQuickAffordanceCoreStartable
@@ -83,7 +86,8 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() {
val emission = MutableStateFlow(mapOf("testQuickAffordanceKey" to listOf(config)))
whenever(keyguardQuickAffordanceRepository.selections).thenReturn(emission)
testScope = TestScope()
testDispatcher = StandardTestDispatcher()
testScope = TestScope(testDispatcher)
underTest = MuteQuickAffordanceCoreStartable(
featureFlags,
@@ -91,7 +95,8 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() {
ringerModeTracker,
userFileManager,
keyguardQuickAffordanceRepository,
testScope,
testScope.backgroundScope,
testDispatcher,
)
}
@@ -158,6 +163,7 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() {
runCurrent()
verify(ringerModeInternal).observeForever(observerCaptor.capture())
observerCaptor.value.onChanged(newRingerMode)
runCurrent()
val result = sharedPrefs.getInt("key_last_non_silent_ringer_mode", -1)
//then