Merge "Revert "Add Mute custom quick affordance"" into tm-qpr-dev am: 5e88838f9b
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21057343 Change-Id: Ie33cc22ca409ff22da19c6e7ae717045f1cc6c2b Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -30,7 +30,6 @@ import com.android.systemui.dagger.qualifiers.PerUser
|
|||||||
import com.android.systemui.globalactions.GlobalActionsComponent
|
import com.android.systemui.globalactions.GlobalActionsComponent
|
||||||
import com.android.systemui.keyboard.KeyboardUI
|
import com.android.systemui.keyboard.KeyboardUI
|
||||||
import com.android.systemui.keyguard.KeyguardViewMediator
|
import com.android.systemui.keyguard.KeyguardViewMediator
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable
|
|
||||||
import com.android.systemui.log.SessionTracker
|
import com.android.systemui.log.SessionTracker
|
||||||
import com.android.systemui.media.dialog.MediaOutputSwitcherDialogUI
|
import com.android.systemui.media.dialog.MediaOutputSwitcherDialogUI
|
||||||
import com.android.systemui.media.RingtonePlayer
|
import com.android.systemui.media.RingtonePlayer
|
||||||
@@ -287,12 +286,4 @@ abstract class SystemUICoreStartableModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@ClassKey(StylusUsiPowerStartable::class)
|
@ClassKey(StylusUsiPowerStartable::class)
|
||||||
abstract fun bindStylusUsiPowerStartable(sysui: StylusUsiPowerStartable): CoreStartable
|
abstract fun bindStylusUsiPowerStartable(sysui: StylusUsiPowerStartable): CoreStartable
|
||||||
|
|
||||||
/** Inject into MuteQuickAffordanceCoreStartable*/
|
|
||||||
@Binds
|
|
||||||
@IntoMap
|
|
||||||
@ClassKey(MuteQuickAffordanceCoreStartable::class)
|
|
||||||
abstract fun bindMuteQuickAffordanceCoreStartable(
|
|
||||||
sysui: MuteQuickAffordanceCoreStartable
|
|
||||||
): CoreStartable
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ object BuiltInKeyguardQuickAffordanceKeys {
|
|||||||
const val DO_NOT_DISTURB = "do_not_disturb"
|
const val DO_NOT_DISTURB = "do_not_disturb"
|
||||||
const val FLASHLIGHT = "flashlight"
|
const val FLASHLIGHT = "flashlight"
|
||||||
const val HOME_CONTROLS = "home"
|
const val HOME_CONTROLS = "home"
|
||||||
const val MUTE = "mute"
|
|
||||||
const val QR_CODE_SCANNER = "qr_code_scanner"
|
const val QR_CODE_SCANNER = "qr_code_scanner"
|
||||||
const val QUICK_ACCESS_WALLET = "wallet"
|
const val QUICK_ACCESS_WALLET = "wallet"
|
||||||
const val VIDEO_CAMERA = "video_camera"
|
const val VIDEO_CAMERA = "video_camera"
|
||||||
|
|||||||
@@ -33,13 +33,12 @@ interface KeyguardDataQuickAffordanceModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@ElementsIntoSet
|
@ElementsIntoSet
|
||||||
fun quickAffordanceConfigs(
|
fun quickAffordanceConfigs(
|
||||||
camera: CameraQuickAffordanceConfig,
|
|
||||||
doNotDisturb: DoNotDisturbQuickAffordanceConfig,
|
doNotDisturb: DoNotDisturbQuickAffordanceConfig,
|
||||||
flashlight: FlashlightQuickAffordanceConfig,
|
flashlight: FlashlightQuickAffordanceConfig,
|
||||||
home: HomeControlsKeyguardQuickAffordanceConfig,
|
home: HomeControlsKeyguardQuickAffordanceConfig,
|
||||||
mute: MuteQuickAffordanceConfig,
|
|
||||||
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
|
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
|
||||||
qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
|
qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
|
||||||
|
camera: CameraQuickAffordanceConfig,
|
||||||
videoCamera: VideoCameraQuickAffordanceConfig,
|
videoCamera: VideoCameraQuickAffordanceConfig,
|
||||||
): Set<KeyguardQuickAffordanceConfig> {
|
): Set<KeyguardQuickAffordanceConfig> {
|
||||||
return setOf(
|
return setOf(
|
||||||
@@ -47,7 +46,6 @@ interface KeyguardDataQuickAffordanceModule {
|
|||||||
doNotDisturb,
|
doNotDisturb,
|
||||||
flashlight,
|
flashlight,
|
||||||
home,
|
home,
|
||||||
mute,
|
|
||||||
quickAccessWallet,
|
quickAccessWallet,
|
||||||
qrCodeScanner,
|
qrCodeScanner,
|
||||||
videoCamera,
|
videoCamera,
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -146,7 +146,7 @@ constructor(
|
|||||||
* Returns a snapshot of the [KeyguardQuickAffordanceConfig] instances of the affordances at the
|
* 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.
|
* slot with the given ID. The configs are sorted in descending priority order.
|
||||||
*/
|
*/
|
||||||
fun getCurrentSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
|
fun getSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
|
||||||
val selections = selectionManager.value.getSelections().getOrDefault(slotId, emptyList())
|
val selections = selectionManager.value.getSelections().getOrDefault(slotId, emptyList())
|
||||||
return configs.filter { selections.contains(it.key) }
|
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
|
* Returns a snapshot of the IDs of the selected affordances, indexed by slot ID. The configs
|
||||||
* are sorted in descending priority order.
|
* are sorted in descending priority order.
|
||||||
*/
|
*/
|
||||||
fun getCurrentSelections(): Map<String, List<String>> {
|
fun getSelections(): Map<String, List<String>> {
|
||||||
return selectionManager.value.getSelections()
|
return selectionManager.value.getSelections()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ constructor(
|
|||||||
private inner class Dumpster : Dumpable {
|
private inner class Dumpster : Dumpable {
|
||||||
override fun dump(pw: PrintWriter, args: Array<out String>) {
|
override fun dump(pw: PrintWriter, args: Array<out String>) {
|
||||||
val slotPickerRepresentations = getSlotPickerRepresentations()
|
val slotPickerRepresentations = getSlotPickerRepresentations()
|
||||||
val selectionsBySlotId = getCurrentSelections()
|
val selectionsBySlotId = getSelections()
|
||||||
pw.println("Slots & selections:")
|
pw.println("Slots & selections:")
|
||||||
slotPickerRepresentations.forEach { slotPickerRepresentation ->
|
slotPickerRepresentations.forEach { slotPickerRepresentation ->
|
||||||
val slotId = slotPickerRepresentation.id
|
val slotId = slotPickerRepresentation.id
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ constructor(
|
|||||||
val slots = repository.get().getSlotPickerRepresentations()
|
val slots = repository.get().getSlotPickerRepresentations()
|
||||||
val slot = slots.find { it.id == slotId } ?: return false
|
val slot = slots.find { it.id == slotId } ?: return false
|
||||||
val selections =
|
val selections =
|
||||||
repository.get().getCurrentSelections().getOrDefault(slotId, emptyList()).toMutableList()
|
repository.get().getSelections().getOrDefault(slotId, emptyList()).toMutableList()
|
||||||
val alreadySelected = selections.remove(affordanceId)
|
val alreadySelected = selections.remove(affordanceId)
|
||||||
if (!alreadySelected) {
|
if (!alreadySelected) {
|
||||||
while (selections.size > 0 && selections.size >= slot.maxSelectedAffordances) {
|
while (selections.size > 0 && selections.size >= slot.maxSelectedAffordances) {
|
||||||
@@ -193,7 +193,7 @@ constructor(
|
|||||||
|
|
||||||
if (affordanceId.isNullOrEmpty()) {
|
if (affordanceId.isNullOrEmpty()) {
|
||||||
return if (
|
return if (
|
||||||
repository.get().getCurrentSelections().getOrDefault(slotId, emptyList()).isEmpty()
|
repository.get().getSelections().getOrDefault(slotId, emptyList()).isEmpty()
|
||||||
) {
|
) {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
@@ -203,7 +203,7 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val selections =
|
val selections =
|
||||||
repository.get().getCurrentSelections().getOrDefault(slotId, emptyList()).toMutableList()
|
repository.get().getSelections().getOrDefault(slotId, emptyList()).toMutableList()
|
||||||
return if (selections.remove(affordanceId)) {
|
return if (selections.remove(affordanceId)) {
|
||||||
repository
|
repository
|
||||||
.get()
|
.get()
|
||||||
@@ -220,7 +220,7 @@ constructor(
|
|||||||
/** Returns affordance IDs indexed by slot ID, for all known slots. */
|
/** Returns affordance IDs indexed by slot ID, for all known slots. */
|
||||||
suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
|
suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
|
||||||
val slots = repository.get().getSlotPickerRepresentations()
|
val slots = repository.get().getSlotPickerRepresentations()
|
||||||
val selections = repository.get().getCurrentSelections()
|
val selections = repository.get().getSelections()
|
||||||
val affordanceById =
|
val affordanceById =
|
||||||
getAffordancePickerRepresentations().associateBy { affordance -> affordance.id }
|
getAffordancePickerRepresentations().associateBy { affordance -> affordance.id }
|
||||||
return slots.associate { slot ->
|
return slots.associate { slot ->
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -275,10 +275,10 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
|
|||||||
expected: Map<String, List<KeyguardQuickAffordanceConfig>>,
|
expected: Map<String, List<KeyguardQuickAffordanceConfig>>,
|
||||||
) {
|
) {
|
||||||
assertThat(observed).isEqualTo(expected)
|
assertThat(observed).isEqualTo(expected)
|
||||||
assertThat(underTest.getCurrentSelections())
|
assertThat(underTest.getSelections())
|
||||||
.isEqualTo(expected.mapValues { (_, configs) -> configs.map { it.key } })
|
.isEqualTo(expected.mapValues { (_, configs) -> configs.map { it.key } })
|
||||||
expected.forEach { (slotId, configs) ->
|
expected.forEach { (slotId, configs) ->
|
||||||
assertThat(underTest.getCurrentSelections(slotId)).isEqualTo(configs)
|
assertThat(underTest.getSelections(slotId)).isEqualTo(configs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user