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.ContentDescription
import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Icon
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.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker import com.android.systemui.settings.UserTracker
import com.android.systemui.util.RingerModeTracker import com.android.systemui.util.RingerModeTracker
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject import javax.inject.Inject
@SysUISingleton @SysUISingleton
@@ -46,6 +54,9 @@ class MuteQuickAffordanceConfig @Inject constructor(
private val userFileManager: UserFileManager, private val userFileManager: UserFileManager,
private val ringerModeTracker: RingerModeTracker, private val ringerModeTracker: RingerModeTracker,
private val audioManager: AudioManager, private val audioManager: AudioManager,
@Application private val coroutineScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : KeyguardQuickAffordanceConfig { ) : KeyguardQuickAffordanceConfig {
private var previousNonSilentMode: Int = DEFAULT_LAST_NON_SILENT_VALUE private var previousNonSilentMode: Int = DEFAULT_LAST_NON_SILENT_VALUE
@@ -58,7 +69,7 @@ class MuteQuickAffordanceConfig @Inject constructor(
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> = override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
ringerModeTracker.ringerModeInternal.asFlow() ringerModeTracker.ringerModeInternal.asFlow()
.onStart { emit(getLastNonSilentRingerMode()) } .onStart { getLastNonSilentRingerMode() }
.distinctUntilChanged() .distinctUntilChanged()
.onEach { mode -> .onEach { mode ->
// only remember last non-SILENT ringer mode // only remember last non-SILENT ringer mode
@@ -87,13 +98,14 @@ class MuteQuickAffordanceConfig @Inject constructor(
activationState, activationState,
) )
} }
.flowOn(backgroundDispatcher)
override fun onTriggered( override fun onTriggered(
expandable: Expandable? expandable: Expandable?
): KeyguardQuickAffordanceConfig.OnTriggeredResult { ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
coroutineScope.launch(backgroundDispatcher) {
val newRingerMode: Int val newRingerMode: Int
val currentRingerMode = val currentRingerMode = audioManager.ringerModeInternal
ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE
if (currentRingerMode == AudioManager.RINGER_MODE_SILENT) { if (currentRingerMode == AudioManager.RINGER_MODE_SILENT) {
newRingerMode = previousNonSilentMode newRingerMode = previousNonSilentMode
} else { } else {
@@ -104,21 +116,25 @@ class MuteQuickAffordanceConfig @Inject constructor(
if (currentRingerMode != newRingerMode) { if (currentRingerMode != newRingerMode) {
audioManager.ringerModeInternal = newRingerMode audioManager.ringerModeInternal = newRingerMode
} }
}
return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
} }
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState = override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
withContext(backgroundDispatcher) {
if (audioManager.isVolumeFixed) { if (audioManager.isVolumeFixed) {
KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
} else { } else {
KeyguardQuickAffordanceConfig.PickerScreenState.Default() KeyguardQuickAffordanceConfig.PickerScreenState.Default()
} }
}
/** /**
* Gets the last non-silent ringer mode from shared-preferences if it exists. This is * Gets the last non-silent ringer mode from shared-preferences if it exists. This is
* cached by [MuteQuickAffordanceCoreStartable] while this affordance is selected * cached by [MuteQuickAffordanceCoreStartable] while this affordance is selected
*/ */
private fun getLastNonSilentRingerMode(): Int = private suspend fun getLastNonSilentRingerMode(): Int =
withContext(backgroundDispatcher) {
userFileManager.getSharedPreferences( userFileManager.getSharedPreferences(
MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME, MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME,
Context.MODE_PRIVATE, Context.MODE_PRIVATE,
@@ -127,6 +143,7 @@ class MuteQuickAffordanceConfig @Inject constructor(
LAST_NON_SILENT_RINGER_MODE_KEY, LAST_NON_SILENT_RINGER_MODE_KEY,
ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE
) )
}
private fun <T> LiveData<T>.asFlow(): Flow<T?> = private fun <T> LiveData<T>.asFlow(): Flow<T?> =
conflatedCallbackFlow { conflatedCallbackFlow {
@@ -134,7 +151,7 @@ class MuteQuickAffordanceConfig @Inject constructor(
observeForever(observer) observeForever(observer)
send(value) send(value)
awaitClose { removeObserver(observer) } awaitClose { removeObserver(observer) }
} }.flowOn(mainDispatcher)
companion object { companion object {
const val LAST_NON_SILENT_RINGER_MODE_KEY = "key_last_non_silent_ringer_mode" 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.CoreStartable
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.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker import com.android.systemui.settings.UserTracker
import com.android.systemui.util.RingerModeTracker import com.android.systemui.util.RingerModeTracker
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
/** /**
@@ -45,6 +48,7 @@ class MuteQuickAffordanceCoreStartable @Inject constructor(
private val userFileManager: UserFileManager, private val userFileManager: UserFileManager,
private val keyguardQuickAffordanceRepository: KeyguardQuickAffordanceRepository, private val keyguardQuickAffordanceRepository: KeyguardQuickAffordanceRepository,
@Application private val coroutineScope: CoroutineScope, @Application private val coroutineScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : CoreStartable { ) : CoreStartable {
private val observer = Observer(this::updateLastNonSilentRingerMode) private val observer = Observer(this::updateLastNonSilentRingerMode)
@@ -72,6 +76,7 @@ class MuteQuickAffordanceCoreStartable @Inject constructor(
} }
private fun updateLastNonSilentRingerMode(lastRingerMode: Int) { private fun updateLastNonSilentRingerMode(lastRingerMode: Int) {
coroutineScope.launch(backgroundDispatcher) {
if (AudioManager.RINGER_MODE_SILENT != lastRingerMode) { if (AudioManager.RINGER_MODE_SILENT != lastRingerMode) {
userFileManager.getSharedPreferences( userFileManager.getSharedPreferences(
MuteQuickAffordanceConfig.MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME, MuteQuickAffordanceConfig.MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME,
@@ -83,4 +88,5 @@ class MuteQuickAffordanceCoreStartable @Inject constructor(
.apply() .apply()
} }
} }
}
} }

View File

@@ -19,7 +19,6 @@ package com.android.systemui.keyguard.data.quickaffordance
import android.content.Context import android.content.Context
import android.media.AudioManager import android.media.AudioManager
import androidx.lifecycle.LiveData
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserFileManager 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.RingerModeTracker
import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
@@ -57,13 +58,15 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
@Mock @Mock
private lateinit var userFileManager: UserFileManager private lateinit var userFileManager: UserFileManager
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope private lateinit var testScope: TestScope
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
testScope = TestScope() testDispatcher = StandardTestDispatcher()
testScope = TestScope(testDispatcher)
whenever(userTracker.userContext).thenReturn(context) whenever(userTracker.userContext).thenReturn(context)
whenever(userFileManager.getSharedPreferences(any(), any(), any())) whenever(userFileManager.getSharedPreferences(any(), any(), any()))
@@ -74,7 +77,10 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
userTracker, userTracker,
userFileManager, userFileManager,
ringerModeTracker, ringerModeTracker,
audioManager audioManager,
testScope.backgroundScope,
testDispatcher,
testDispatcher,
) )
} }
@@ -103,17 +109,16 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
} }
@Test @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 //given
val ringerModeCapture = argumentCaptor<Int>() val ringerModeCapture = argumentCaptor<Int>()
val ringerModeInternal = mock<LiveData<Int>>() whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
whenever(ringerModeInternal.value).thenReturn(AudioManager.RINGER_MODE_NORMAL)
underTest.onTriggered(null) underTest.onTriggered(null)
whenever(ringerModeInternal.value).thenReturn(AudioManager.RINGER_MODE_SILENT) whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
//when //when
val result = underTest.onTriggered(null) val result = underTest.onTriggered(null)
runCurrent()
verify(audioManager, times(2)).ringerModeInternal = ringerModeCapture.capture() verify(audioManager, times(2)).ringerModeInternal = ringerModeCapture.capture()
//then //then
@@ -122,15 +127,14 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
} }
@Test @Test
fun `triggered - state is not SILENT - move to SILENT ringer`() { fun `triggered - state is not SILENT - move to SILENT ringer`() = testScope.runTest {
//given //given
val ringerModeCapture = argumentCaptor<Int>() val ringerModeCapture = argumentCaptor<Int>()
val ringerModeInternal = mock<LiveData<Int>>() whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
whenever(ringerModeInternal.value).thenReturn(AudioManager.RINGER_MODE_NORMAL)
//when //when
val result = underTest.onTriggered(null) val result = underTest.onTriggered(null)
runCurrent()
verify(audioManager).ringerModeInternal = ringerModeCapture.capture() verify(audioManager).ringerModeInternal = ringerModeCapture.capture()
//then //then

View File

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