Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 278bfa4f authored by Brad Hinegardner's avatar Brad Hinegardner Committed by Automerger Merge Worker
Browse files

Merge "Add Mute custom quick affordance" into tm-qpr-dev am: d886dac3 am: 6318005a

parents 77ce75fa 6318005a
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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"
+3 −1
Original line number Diff line number Diff line
@@ -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,
+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
+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