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

Commit 0087eef2 authored by Matías Hernández's avatar Matías Hernández
Browse files

Properly support multi-user in NotificationSettingsRepository

(And also in every place that injected SecureSettingsRepository / SystemSettingsRepository).

This also:
* adds some methods to UserAwareSecureSettingsRepository (via the base class) and their tests.
* removes the UserAwareSecureSettingsRepository interface/impl/fake split.
* adds a UserAwareSystemSettingsRepository (same base class, but interacting with SystemSettings instead of SecureSettings).

Bug: 356099784
Test: manual, as described in b/356099784
Flag: com.android.systemui.user_aware_settings_repositories
Change-Id: Ic8d759de9cc93e59ae279ecce0c818b1f3d200e3
parent 8f6d58fd
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1507,6 +1507,16 @@ flag {
  }
}

flag {
  namespace: "systemui"
  name: "user_aware_settings_repositories"
  description: "Provide user-aware versions of SecureSettingsRepository and SystemSettingsRepository in SystemUI modules (see doc linked from b/356099784)."
  bug: "356099784"
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}

flag {
    name: "notify_password_text_view_user_activity_in_background"
    namespace: "systemui"
+4 −87
Original line number Diff line number Diff line
@@ -16,102 +16,19 @@

package com.android.systemui.shared.settings.data.repository

import android.content.ContentResolver
import android.database.ContentObserver
import android.provider.Settings
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext

/**
 * Defines interface for classes that can provide access to data from [Settings.Secure].
 * This repository doesn't guarantee to provide value across different users. For that
 * see: [UserAwareSecureSettingsRepository]
 */
/** Defines interface for classes that can provide access to data from [Settings.Secure]. */
interface SecureSettingsRepository {

    /** Returns a [Flow] tracking the value of a setting as an [Int]. */
    fun intSetting(
        name: String,
        defaultValue: Int = 0,
    ): Flow<Int>
    fun intSetting(name: String, defaultValue: Int = 0): Flow<Int>

    /** Updates the value of the setting with the given name. */
    suspend fun setInt(
        name: String,
        value: Int,
    )
    suspend fun setInt(name: String, value: Int)

    suspend fun getInt(
        name: String,
        defaultValue: Int = 0,
    ): Int
    suspend fun getInt(name: String, defaultValue: Int = 0): Int

    suspend fun getString(name: String): String?
}

class SecureSettingsRepositoryImpl(
    private val contentResolver: ContentResolver,
    private val backgroundDispatcher: CoroutineDispatcher,
) : SecureSettingsRepository {

    override fun intSetting(
        name: String,
        defaultValue: Int,
    ): Flow<Int> {
        return callbackFlow {
                val observer =
                    object : ContentObserver(null) {
                        override fun onChange(selfChange: Boolean) {
                            trySend(Unit)
                        }
                    }

                contentResolver.registerContentObserver(
                    Settings.Secure.getUriFor(name),
                    /* notifyForDescendants= */ false,
                    observer,
                )
                send(Unit)

                awaitClose { contentResolver.unregisterContentObserver(observer) }
            }
            .map { Settings.Secure.getInt(contentResolver, name, defaultValue) }
            // The above work is done on the background thread (which is important for accessing
            // settings through the content resolver).
            .flowOn(backgroundDispatcher)
    }

    override suspend fun setInt(name: String, value: Int) {
        withContext(backgroundDispatcher) {
            Settings.Secure.putInt(
                contentResolver,
                name,
                value,
            )
        }
    }

    override suspend fun getInt(name: String, defaultValue: Int): Int {
        return withContext(backgroundDispatcher) {
            Settings.Secure.getInt(
                contentResolver,
                name,
                defaultValue,
            )
        }
    }

    override suspend fun getString(name: String): String? {
        return withContext(backgroundDispatcher) {
            Settings.Secure.getString(
                contentResolver,
                name,
            )
        }
    }
}
+81 −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.shared.settings.data.repository

import android.content.ContentResolver
import android.database.ContentObserver
import android.provider.Settings
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext

/**
 * Simple implementation of [SecureSettingsRepository].
 *
 * This repository doesn't guarantee to provide value across different users, and therefore
 * shouldn't be used in SystemUI. For that see: [UserAwareSecureSettingsRepository]
 */
// TODO: b/377244768 - Move to Theme/WallpaperPicker, away from SystemUI.
class SecureSettingsRepositoryImpl(
    private val contentResolver: ContentResolver,
    private val backgroundDispatcher: CoroutineDispatcher,
) : SecureSettingsRepository {

    override fun intSetting(name: String, defaultValue: Int): Flow<Int> {
        return callbackFlow {
                val observer =
                    object : ContentObserver(null) {
                        override fun onChange(selfChange: Boolean) {
                            trySend(Unit)
                        }
                    }

                contentResolver.registerContentObserver(
                    Settings.Secure.getUriFor(name),
                    /* notifyForDescendants= */ false,
                    observer,
                )
                send(Unit)

                awaitClose { contentResolver.unregisterContentObserver(observer) }
            }
            .map { Settings.Secure.getInt(contentResolver, name, defaultValue) }
            // The above work is done on the background thread (which is important for accessing
            // settings through the content resolver).
            .flowOn(backgroundDispatcher)
    }

    override suspend fun setInt(name: String, value: Int) {
        withContext(backgroundDispatcher) { Settings.Secure.putInt(contentResolver, name, value) }
    }

    override suspend fun getInt(name: String, defaultValue: Int): Int {
        return withContext(backgroundDispatcher) {
            Settings.Secure.getInt(contentResolver, name, defaultValue)
        }
    }

    override suspend fun getString(name: String): String? {
        return withContext(backgroundDispatcher) {
            Settings.Secure.getString(contentResolver, name)
        }
    }
}
+4 −87
Original line number Diff line number Diff line
@@ -16,102 +16,19 @@

package com.android.systemui.shared.settings.data.repository

import android.content.ContentResolver
import android.database.ContentObserver
import android.provider.Settings
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext

/**
 * Defines interface for classes that can provide access to data from [Settings.System]. This
 * repository doesn't guarantee to provide value across different users. For that see:
 * [UserAwareSecureSettingsRepository] which does that for secure settings.
 */
/** Interface for classes that can provide access to data from [Settings.System]. */
interface SystemSettingsRepository {

    /** Returns a [Flow] tracking the value of a setting as an [Int]. */
    fun intSetting(
        name: String,
        defaultValue: Int = 0,
    ): Flow<Int>
    fun intSetting(name: String, defaultValue: Int = 0): Flow<Int>

    /** Updates the value of the setting with the given name. */
    suspend fun setInt(
        name: String,
        value: Int,
    )
    suspend fun setInt(name: String, value: Int)

    suspend fun getInt(
        name: String,
        defaultValue: Int = 0,
    ): Int
    suspend fun getInt(name: String, defaultValue: Int = 0): Int

    suspend fun getString(name: String): String?
}

class SystemSettingsRepositoryImpl(
    private val contentResolver: ContentResolver,
    private val backgroundDispatcher: CoroutineDispatcher,
) : SystemSettingsRepository {

    override fun intSetting(
        name: String,
        defaultValue: Int,
    ): Flow<Int> {
        return callbackFlow {
                val observer =
                    object : ContentObserver(null) {
                        override fun onChange(selfChange: Boolean) {
                            trySend(Unit)
                        }
                    }

                contentResolver.registerContentObserver(
                    Settings.System.getUriFor(name),
                    /* notifyForDescendants= */ false,
                    observer,
                )
                send(Unit)

                awaitClose { contentResolver.unregisterContentObserver(observer) }
            }
            .map { Settings.System.getInt(contentResolver, name, defaultValue) }
            // The above work is done on the background thread (which is important for accessing
            // settings through the content resolver).
            .flowOn(backgroundDispatcher)
    }

    override suspend fun setInt(name: String, value: Int) {
        withContext(backgroundDispatcher) {
            Settings.System.putInt(
                contentResolver,
                name,
                value,
            )
        }
    }

    override suspend fun getInt(name: String, defaultValue: Int): Int {
        return withContext(backgroundDispatcher) {
            Settings.System.getInt(
                contentResolver,
                name,
                defaultValue,
            )
        }
    }

    override suspend fun getString(name: String): String? {
        return withContext(backgroundDispatcher) {
            Settings.System.getString(
                contentResolver,
                name,
            )
        }
    }
}
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.shared.settings.data.repository

import android.content.ContentResolver
import android.database.ContentObserver
import android.provider.Settings
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext

/**
 * Defines interface for classes that can provide access to data from [Settings.System].
 *
 * This repository doesn't guarantee to provide value across different users. For that see:
 * [UserAwareSystemSettingsRepository].
 */
// TODO: b/377244768 - Move to Theme/WallpaperPicker, away from SystemUI.
class SystemSettingsRepositoryImpl(
    private val contentResolver: ContentResolver,
    private val backgroundDispatcher: CoroutineDispatcher,
) : SystemSettingsRepository {

    override fun intSetting(name: String, defaultValue: Int): Flow<Int> {
        return callbackFlow {
                val observer =
                    object : ContentObserver(null) {
                        override fun onChange(selfChange: Boolean) {
                            trySend(Unit)
                        }
                    }

                contentResolver.registerContentObserver(
                    Settings.System.getUriFor(name),
                    /* notifyForDescendants= */ false,
                    observer,
                )
                send(Unit)

                awaitClose { contentResolver.unregisterContentObserver(observer) }
            }
            .map { Settings.System.getInt(contentResolver, name, defaultValue) }
            // The above work is done on the background thread (which is important for accessing
            // settings through the content resolver).
            .flowOn(backgroundDispatcher)
    }

    override suspend fun setInt(name: String, value: Int) {
        withContext(backgroundDispatcher) { Settings.System.putInt(contentResolver, name, value) }
    }

    override suspend fun getInt(name: String, defaultValue: Int): Int {
        return withContext(backgroundDispatcher) {
            Settings.System.getInt(contentResolver, name, defaultValue)
        }
    }

    override suspend fun getString(name: String): String? {
        return withContext(backgroundDispatcher) {
            Settings.System.getString(contentResolver, name)
        }
    }
}
Loading