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

Commit 66883bf0 authored by Olivier St-Onge's avatar Olivier St-Onge Committed by Android (Google) Code Review
Browse files

Merge "Implement backup and restore for large tiles specs" into main

parents 6df9c2b0 77d970d8
Loading
Loading
Loading
Loading
+0 −61
Original line number Diff line number Diff line
@@ -104,67 +104,6 @@ class QSPreferencesRepositoryTest : SysuiTestCase() {
            }
        }

    @Test
    fun showLabels_updatesFromSharedPreferences() =
        with(kosmos) {
            testScope.runTest {
                val latest by collectLastValue(underTest.showLabels)
                assertThat(latest).isFalse()

                setShowLabelsInSharedPreferences(true)
                assertThat(latest).isTrue()

                setShowLabelsInSharedPreferences(false)
                assertThat(latest).isFalse()
            }
        }

    @Test
    fun showLabels_updatesFromUserChange() =
        with(kosmos) {
            testScope.runTest {
                fakeUserRepository.setUserInfos(USERS)
                val latest by collectLastValue(underTest.showLabels)

                fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
                setShowLabelsInSharedPreferences(false)

                fakeUserRepository.setSelectedUserInfo(ANOTHER_USER)
                setShowLabelsInSharedPreferences(true)

                fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
                assertThat(latest).isFalse()
            }
        }

    @Test
    fun setShowLabels_inSharedPreferences() {
        underTest.setShowLabels(false)
        assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()

        underTest.setShowLabels(true)
        assertThat(getShowLabelsFromSharedPreferences(false)).isTrue()
    }

    @Test
    fun setShowLabels_forDifferentUser() =
        with(kosmos) {
            testScope.runTest {
                fakeUserRepository.setUserInfos(USERS)

                fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
                underTest.setShowLabels(false)
                assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()

                fakeUserRepository.setSelectedUserInfo(ANOTHER_USER)
                underTest.setShowLabels(true)
                assertThat(getShowLabelsFromSharedPreferences(false)).isTrue()

                fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
                assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()
            }
        }

    private fun getSharedPreferences(): SharedPreferences =
        with(kosmos) {
            return userFileManager.getSharedPreferences(
+26 −30
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper
import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
import com.android.systemui.keyguard.domain.backup.KeyguardQuickAffordanceBackupHelper
import com.android.systemui.people.widget.PeopleBackupHelper
import com.android.systemui.qs.panels.domain.backup.QSPreferencesBackupHelper
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManagerImpl

@@ -58,9 +59,9 @@ open class BackupHelper : BackupAgentHelper() {
        private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences"
        private const val KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY =
            "systemui.keyguard.quickaffordance.shared_preferences"
        private const val COMMUNAL_PREFS_BACKUP_KEY =
            "systemui.communal.shared_preferences"
        private const val COMMUNAL_PREFS_BACKUP_KEY = "systemui.communal.shared_preferences"
        private const val COMMUNAL_STATE_BACKUP_KEY = "systemui.communal_state"
        private const val QS_PREFERENCES_BACKUP_KEY = "systemui.qs.shared_preferences"
        val controlsDataLock = Any()
        const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED"
        const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
@@ -74,22 +75,20 @@ open class BackupHelper : BackupAgentHelper() {
        val keys = PeopleBackupHelper.getFilesToBackup()
        addHelper(
            PEOPLE_TILES_BACKUP_KEY,
            PeopleBackupHelper(this, userHandle, keys.toTypedArray())
            PeopleBackupHelper(this, userHandle, keys.toTypedArray()),
        )
        addHelper(
            KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY,
            KeyguardQuickAffordanceBackupHelper(
                context = this,
                userId = userHandle.identifier,
            ),
            KeyguardQuickAffordanceBackupHelper(context = this, userId = userHandle.identifier),
        )
        addHelper(
            QS_PREFERENCES_BACKUP_KEY,
            QSPreferencesBackupHelper(context = this, userId = userHandle.identifier),
        )
        if (communalEnabled()) {
            addHelper(
                COMMUNAL_PREFS_BACKUP_KEY,
                CommunalPrefsBackupHelper(
                    context = this,
                    userId = userHandle.identifier,
                )
                CommunalPrefsBackupHelper(context = this, userId = userHandle.identifier),
            )
            addHelper(
                COMMUNAL_STATE_BACKUP_KEY,
@@ -110,10 +109,7 @@ open class BackupHelper : BackupAgentHelper() {
    }

    private fun addControlsHelper(userId: Int) {
        val file = UserFileManagerImpl.createFile(
            userId = userId,
            fileName = CONTROLS,
        )
        val file = UserFileManagerImpl.createFile(userId = userId, fileName = CONTROLS)
        // The map in mapOf is guaranteed to be order preserving
        val controlsMap = mapOf(file.getPath() to getPPControlsFile(this, userId))
        NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also {
@@ -134,6 +130,7 @@ open class BackupHelper : BackupAgentHelper() {
     * @property lock a lock to hold while backing up and restoring the files.
     * @property context the context of the [BackupAgent]
     * @property fileNamesAndPostProcess a map from the filenames to back up and the post processing
     *
     * ```
     *                                   actions to take
     * ```
@@ -141,7 +138,7 @@ open class BackupHelper : BackupAgentHelper() {
    private class NoOverwriteFileBackupHelper(
        val lock: Any,
        val context: Context,
        val fileNamesAndPostProcess: Map<String, () -> Unit>
        val fileNamesAndPostProcess: Map<String, () -> Unit>,
    ) : FileBackupHelper(context, *fileNamesAndPostProcess.keys.toTypedArray()) {

        override fun restoreEntity(data: BackupDataInputStream) {
@@ -152,11 +149,12 @@ open class BackupHelper : BackupAgentHelper() {
                return
            }
            synchronized(lock) {
                traceSection("File restore: ${data.key}") {
                    super.restoreEntity(data)
                }
                Log.d(TAG, "Finishing restore for ${data.key} for user ${context.userId}. " +
                        "Starting postProcess.")
                traceSection("File restore: ${data.key}") { super.restoreEntity(data) }
                Log.d(
                    TAG,
                    "Finishing restore for ${data.key} for user ${context.userId}. " +
                        "Starting postProcess.",
                )
                traceSection("Postprocess: ${data.key}") {
                    fileNamesAndPostProcess.get(data.key)?.invoke()
                }
@@ -167,7 +165,7 @@ open class BackupHelper : BackupAgentHelper() {
        override fun performBackup(
            oldState: ParcelFileDescriptor?,
            data: BackupDataOutput?,
            newState: ParcelFileDescriptor?
            newState: ParcelFileDescriptor?,
        ) {
            synchronized(lock) { super.performBackup(oldState, data, newState) }
        }
@@ -176,12 +174,10 @@ open class BackupHelper : BackupAgentHelper() {

private fun getPPControlsFile(context: Context, userId: Int): () -> Unit {
    return {
        val file = UserFileManagerImpl.createFile(
            userId = userId,
            fileName = BackupHelper.CONTROLS,
        )
        val file = UserFileManagerImpl.createFile(userId = userId, fileName = BackupHelper.CONTROLS)
        if (file.exists()) {
            val dest = UserFileManagerImpl.createFile(
            val dest =
                UserFileManagerImpl.createFile(
                    userId = userId,
                    fileName = AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME,
                )
+27 −24
Original line number Diff line number Diff line
@@ -17,9 +17,16 @@
package com.android.systemui.qs.panels.data.repository

import android.content.Context
import android.content.IntentFilter
import android.content.SharedPreferences
import com.android.systemui.backup.BackupHelper
import com.android.systemui.backup.BackupHelper.Companion.ACTION_RESTORE_FINISHED
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.qs.panels.shared.model.PanelsLog
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.settings.UserFileManager
import com.android.systemui.user.data.repository.UserRepository
@@ -29,9 +36,11 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach

/** Repository for QS user preferences. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -43,26 +52,31 @@ constructor(
    private val userRepository: UserRepository,
    private val defaultLargeTilesRepository: DefaultLargeTilesRepository,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
    @PanelsLog private val logBuffer: LogBuffer,
    broadcastDispatcher: BroadcastDispatcher,
) {
    /** Whether to show the labels on icon tiles for the current user. */
    val showLabels: Flow<Boolean> =
        userRepository.selectedUserInfo
            .flatMapLatest { userInfo ->
                val prefs = getSharedPrefs(userInfo.id)
                prefs.observe().emitOnStart().map { prefs.getBoolean(ICON_LABELS_KEY, false) }
            }
            .flowOn(backgroundDispatcher)
    private val logger by lazy { Logger(logBuffer, TAG) }

    private val backupRestorationEvents: Flow<Unit> =
        broadcastDispatcher
            .broadcastFlow(
                filter = IntentFilter(ACTION_RESTORE_FINISHED),
                flags = Context.RECEIVER_NOT_EXPORTED,
                permission = BackupHelper.PERMISSION_SELF,
            )
            .onEach { logger.i("Restored state for QS preferences.") }
            .emitOnStart()

    /** Set of [TileSpec] to display as large tiles for the current user. */
    val largeTilesSpecs: Flow<Set<TileSpec>> =
        userRepository.selectedUserInfo
            .flatMapLatest { userInfo ->
        combine(backupRestorationEvents, userRepository.selectedUserInfo, ::Pair)
            .flatMapLatest { (_, userInfo) ->
                val prefs = getSharedPrefs(userInfo.id)
                prefs.observe().emitOnStart().map {
                    prefs
                        .getStringSet(
                            LARGE_TILES_SPECS_KEY,
                            defaultLargeTilesRepository.defaultLargeTiles.map { it.spec }.toSet()
                            defaultLargeTilesRepository.defaultLargeTiles.map { it.spec }.toSet(),
                        )
                        ?.map { TileSpec.create(it) }
                        ?.toSet() ?: defaultLargeTilesRepository.defaultLargeTiles
@@ -70,13 +84,6 @@ constructor(
            }
            .flowOn(backgroundDispatcher)

    /** Sets for the current user whether to show the labels on icon tiles. */
    fun setShowLabels(showLabels: Boolean) {
        with(getSharedPrefs(userRepository.getSelectedUserInfo().id)) {
            edit().putBoolean(ICON_LABELS_KEY, showLabels).apply()
        }
    }

    /** Sets for the current user the set of [TileSpec] to display as large tiles. */
    fun setLargeTilesSpecs(specs: Set<TileSpec>) {
        with(getSharedPrefs(userRepository.getSelectedUserInfo().id)) {
@@ -85,15 +92,11 @@ constructor(
    }

    private fun getSharedPrefs(userId: Int): SharedPreferences {
        return userFileManager.getSharedPreferences(
            FILE_NAME,
            Context.MODE_PRIVATE,
            userId,
        )
        return userFileManager.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, userId)
    }

    companion object {
        private const val ICON_LABELS_KEY = "show_icon_labels"
        private const val TAG = "QSPreferencesRepository"
        private const val LARGE_TILES_SPECS_KEY = "large_tiles_specs"
        const val FILE_NAME = "quick_settings_prefs"
    }
+28 −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.qs.panels.domain.backup

import android.app.backup.SharedPreferencesBackupHelper
import android.content.Context
import com.android.systemui.qs.panels.data.repository.QSPreferencesRepository.Companion.FILE_NAME
import com.android.systemui.settings.UserFileManagerImpl

class QSPreferencesBackupHelper(context: Context, userId: Int) :
    SharedPreferencesBackupHelper(
        context,
        UserFileManagerImpl.createFile(userId = userId, fileName = FILE_NAME).path,
    )
+5 −1
Original line number Diff line number Diff line
@@ -16,8 +16,10 @@

package com.android.systemui.qs.panels.data.repository

import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.settings.userFileManager
import com.android.systemui.user.data.repository.userRepository

@@ -27,6 +29,8 @@ val Kosmos.qsPreferencesRepository by
            userFileManager,
            userRepository,
            defaultLargeTilesRepository,
            testDispatcher
            testDispatcher,
            FakeLogBuffer.Factory.create(),
            broadcastDispatcher,
        )
    }