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

Commit dc5ae012 authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Android (Google) Code Review
Browse files

Merge "Add repository for controls related settings" into tm-qpr-dev

parents eda61eee b18e4a24
Loading
Loading
Loading
Loading
+29 −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.controls

import kotlinx.coroutines.flow.StateFlow

/** Repository for Device controls related settings. */
interface ControlsSettingsRepository {
    /** Whether device controls activity can be shown above lockscreen for this user. */
    val canShowControlsInLockscreen: StateFlow<Boolean>

    /** Whether trivial controls can be actioned from the lockscreen for this user. */
    val allowActionOnTrivialControlsInLockscreen: StateFlow<Boolean>
}
+90 −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.controls

import android.provider.Settings
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.SettingObserver
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn

/**
 * This implementation uses an `@Application` [CoroutineScope] to provide hot flows for the values
 * of the tracked settings.
 */
@SysUISingleton
class ControlsSettingsRepositoryImpl
@Inject
constructor(
    @Application private val scope: CoroutineScope,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
    private val userRepository: UserRepository,
    private val secureSettings: SecureSettings
) : ControlsSettingsRepository {

    override val canShowControlsInLockscreen =
        makeFlowForSetting(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS)

    override val allowActionOnTrivialControlsInLockscreen =
        makeFlowForSetting(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS)

    @OptIn(ExperimentalCoroutinesApi::class)
    private fun makeFlowForSetting(setting: String): StateFlow<Boolean> {
        return userRepository.selectedUserInfo
            .distinctUntilChanged()
            .flatMapLatest { userInfo ->
                conflatedCallbackFlow {
                        val observer =
                            object : SettingObserver(secureSettings, null, setting, userInfo.id) {
                                override fun handleValueChanged(
                                    value: Int,
                                    observedChange: Boolean
                                ) {
                                    trySend(value == 1)
                                }
                            }
                        observer.isListening = true
                        trySend(observer.value == 1)
                        awaitClose { observer.isListening = false }
                    }
                    .flowOn(backgroundDispatcher)
                    .distinctUntilChanged()
            }
            .stateIn(
                scope,
                started = SharingStarted.Eagerly,
                // When the observer starts listening, the flow will emit the current value
                // so the initialValue here is irrelevant.
                initialValue = false,
            )
    }
}
+14 −45
Original line number Diff line number Diff line
@@ -16,13 +16,10 @@

package com.android.systemui.controls.dagger

import android.content.ContentResolver
import android.content.Context
import android.database.ContentObserver
import android.os.UserHandle
import android.provider.Settings
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import com.android.systemui.controls.ControlsSettingsRepository
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.controller.ControlsTileResourceConfiguration
import com.android.systemui.controls.controller.ControlsTileResourceConfigurationImpl
@@ -31,12 +28,10 @@ import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.settings.SecureSettings
import dagger.Lazy
import kotlinx.coroutines.flow.StateFlow
import java.util.Optional
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow

/**
 * Pseudo-component to inject into classes outside `com.android.systemui.controls`.
@@ -54,39 +49,18 @@ class ControlsComponent @Inject constructor(
        private val lockPatternUtils: LockPatternUtils,
        private val keyguardStateController: KeyguardStateController,
        private val userTracker: UserTracker,
    private val secureSettings: SecureSettings,
    private val optionalControlsTileResourceConfiguration:
        Optional<ControlsTileResourceConfiguration>
        controlsSettingsRepository: ControlsSettingsRepository,
        optionalControlsTileResourceConfiguration: Optional<ControlsTileResourceConfiguration>
) {
    private val contentResolver: ContentResolver
        get() = context.contentResolver

    private val _canShowWhileLockedSetting = MutableStateFlow(false)
    val canShowWhileLockedSetting = _canShowWhileLockedSetting.asStateFlow()
    val canShowWhileLockedSetting: StateFlow<Boolean> =
            controlsSettingsRepository.canShowControlsInLockscreen

    private val controlsTileResourceConfiguration: ControlsTileResourceConfiguration =
        optionalControlsTileResourceConfiguration.orElse(
            ControlsTileResourceConfigurationImpl()
        )

    val showWhileLockedObserver = object : ContentObserver(null) {
        override fun onChange(selfChange: Boolean) {
            updateShowWhileLocked()
        }
    }

    init {
        if (featureEnabled) {
            secureSettings.registerContentObserverForUser(
                Settings.Secure.getUriFor(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS),
                false, /* notifyForDescendants */
                showWhileLockedObserver,
                UserHandle.USER_ALL
            )
            updateShowWhileLocked()
        }
    }

    fun getControlsController(): Optional<ControlsController> {
        return if (featureEnabled) Optional.of(lazyControlsController.get()) else Optional.empty()
    }
@@ -127,11 +101,6 @@ class ControlsComponent @Inject constructor(
        return Visibility.AVAILABLE
    }

    private fun updateShowWhileLocked() {
        _canShowWhileLockedSetting.value = secureSettings.getIntForUser(
            Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
    }

    enum class Visibility {
        AVAILABLE, AVAILABLE_AFTER_UNLOCK, UNAVAILABLE
    }
+7 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.app.Activity
import android.content.pm.PackageManager
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.ControlsMetricsLoggerImpl
import com.android.systemui.controls.ControlsSettingsRepository
import com.android.systemui.controls.ControlsSettingsRepositoryImpl
import com.android.systemui.controls.controller.ControlsBindingController
import com.android.systemui.controls.controller.ControlsBindingControllerImpl
import com.android.systemui.controls.controller.ControlsController
@@ -82,6 +84,11 @@ abstract class ControlsModule {
    @Binds
    abstract fun provideUiController(controller: ControlsUiControllerImpl): ControlsUiController

    @Binds
    abstract fun provideSettingsManager(
            manager: ControlsSettingsRepositoryImpl
    ): ControlsSettingsRepository

    @Binds
    abstract fun provideMetricsLogger(logger: ControlsMetricsLoggerImpl): ControlsMetricsLogger

+9 −43
Original line number Diff line number Diff line
@@ -24,9 +24,6 @@ import android.app.PendingIntent
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.database.ContentObserver
import android.net.Uri
import android.os.Handler
import android.os.UserHandle
import android.os.VibrationEffect
import android.provider.Settings.Secure
@@ -40,6 +37,7 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.ControlsSettingsRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -68,17 +66,17 @@ class ControlActionCoordinatorImpl @Inject constructor(
    private val vibrator: VibratorHelper,
    private val secureSettings: SecureSettings,
    private val userContextProvider: UserContextProvider,
    @Main mainHandler: Handler
    private val controlsSettingsRepository: ControlsSettingsRepository,
) : ControlActionCoordinator {
    private var dialog: Dialog? = null
    private var pendingAction: Action? = null
    private var actionsInProgress = mutableSetOf<String>()
    private val isLocked: Boolean
        get() = !keyguardStateController.isUnlocked()
    private var mAllowTrivialControls: Boolean = secureSettings.getIntForUser(
            Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
    private var mShowDeviceControlsInLockscreen: Boolean = secureSettings.getIntForUser(
            Secure.LOCKSCREEN_SHOW_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
    private val allowTrivialControls: Boolean
        get() = controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen.value
    private val showDeviceControlsInLockscreen: Boolean
        get() = controlsSettingsRepository.canShowControlsInLockscreen.value
    override lateinit var activityContext: Context

    companion object {
@@ -86,38 +84,6 @@ class ControlActionCoordinatorImpl @Inject constructor(
        private const val MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG = 2
    }

    init {
        val lockScreenShowControlsUri =
            secureSettings.getUriFor(Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS)
        val showControlsUri =
                secureSettings.getUriFor(Secure.LOCKSCREEN_SHOW_CONTROLS)
        val controlsContentObserver = object : ContentObserver(mainHandler) {
            override fun onChange(selfChange: Boolean, uri: Uri?) {
                super.onChange(selfChange, uri)
                when (uri) {
                    lockScreenShowControlsUri -> {
                        mAllowTrivialControls = secureSettings.getIntForUser(
                                Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
                                0, UserHandle.USER_CURRENT) != 0
                    }
                    showControlsUri -> {
                        mShowDeviceControlsInLockscreen = secureSettings
                                .getIntForUser(Secure.LOCKSCREEN_SHOW_CONTROLS,
                                        0, UserHandle.USER_CURRENT) != 0
                    }
                }
            }
        }
        secureSettings.registerContentObserverForUser(
            lockScreenShowControlsUri,
            false /* notifyForDescendants */, controlsContentObserver, UserHandle.USER_ALL
        )
        secureSettings.registerContentObserverForUser(
            showControlsUri,
            false /* notifyForDescendants */, controlsContentObserver, UserHandle.USER_ALL
        )
    }

    override fun closeDialogs() {
        dialog?.dismiss()
        dialog = null
@@ -224,7 +190,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
    @AnyThread
    @VisibleForTesting
    fun bouncerOrRun(action: Action) {
        val authRequired = action.authIsRequired || !mAllowTrivialControls
        val authRequired = action.authIsRequired || !allowTrivialControls

        if (keyguardStateController.isShowing() && authRequired) {
            if (isLocked) {
@@ -282,7 +248,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
                PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
        val attempts = prefs.getInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, 0)
        if (attempts >= MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG ||
                (mShowDeviceControlsInLockscreen && mAllowTrivialControls)) {
                (showDeviceControlsInLockscreen && allowTrivialControls)) {
            return
        }
        val builder = AlertDialog
@@ -304,7 +270,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
                    true
                }

        if (mShowDeviceControlsInLockscreen) {
        if (showDeviceControlsInLockscreen) {
            dialog = builder
                    .setTitle(R.string.controls_settings_trivial_controls_dialog_title)
                    .setMessage(R.string.controls_settings_trivial_controls_dialog_message)
Loading