Loading packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +9 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,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 Loading Loading @@ -295,6 +296,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 Loading packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt +1 −0 Original line number Diff line number Diff line Loading @@ -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" Loading packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt +3 −1 Original line number Diff line number Diff line Loading @@ -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( Loading @@ -46,6 +47,7 @@ interface KeyguardDataQuickAffordanceModule { doNotDisturb, flashlight, home, mute, quickAccessWallet, qrCodeScanner, videoCamera, Loading packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt 0 → 100644 +144 −0 Original line number Diff line number Diff line /* * 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 } } No newline at end of file packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt 0 → 100644 +86 −0 Original line number Diff line number Diff line /* * 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() } } } No newline at end of file Loading
packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +9 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,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 Loading Loading @@ -295,6 +296,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 Loading
packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt +1 −0 Original line number Diff line number Diff line Loading @@ -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" Loading
packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt +3 −1 Original line number Diff line number Diff line Loading @@ -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( Loading @@ -46,6 +47,7 @@ interface KeyguardDataQuickAffordanceModule { doNotDisturb, flashlight, home, mute, quickAccessWallet, qrCodeScanner, videoCamera, Loading
packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt 0 → 100644 +144 −0 Original line number Diff line number Diff line /* * 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 } } No newline at end of file
packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt 0 → 100644 +86 −0 Original line number Diff line number Diff line /* * 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() } } } No newline at end of file