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

Commit adb15d44 authored by Olivier St-Onge's avatar Olivier St-Onge
Browse files

Save QS icon labels setting to shared preferences

Bug: 341897086
Test: QSPreferencesRepositoryTest
Test: IconLabelVisibilityInteractorTest
Flag: com.android.systemui.qs_ui_refactor
Change-Id: I1cfa90cff5e6851ce1343b199145336ecd528ee7
parent d0962cf0
Loading
Loading
Loading
Loading
+73 −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.data.repository

import android.content.Context
import android.content.SharedPreferences
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserFileManager
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.SharedPreferencesExt.observe
import com.android.systemui.util.kotlin.emitOnStart
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map

/** Repository for QS user preferences. */
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class QSPreferencesRepository
@Inject
constructor(
    private val userFileManager: UserFileManager,
    private val userRepository: UserRepository,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
) {
    /** 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)

    /** 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()
        }
    }

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

    companion object {
        private const val ICON_LABELS_KEY = "show_icon_labels"
        const val FILE_NAME = "quick_settings_prefs"
    }
}
+4 −5
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.qs.panels.data.repository.IconLabelVisibilityRepository
import com.android.systemui.qs.panels.shared.model.IconLabelVisibilityLog
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -33,17 +32,17 @@ import kotlinx.coroutines.flow.stateIn
class IconLabelVisibilityInteractor
@Inject
constructor(
    private val repo: IconLabelVisibilityRepository,
    private val preferencesInteractor: QSPreferencesInteractor,
    @IconLabelVisibilityLog private val logBuffer: LogBuffer,
    @Application scope: CoroutineScope,
) {
    val showLabels: StateFlow<Boolean> =
        repo.showLabels
        preferencesInteractor.showLabels
            .onEach { logChange(it) }
            .stateIn(scope, SharingStarted.WhileSubscribed(), repo.showLabels.value)
            .stateIn(scope, SharingStarted.WhileSubscribed(), false)

    fun setShowLabels(showLabels: Boolean) {
        repo.setShowLabels(showLabels)
        preferencesInteractor.setShowLabels(showLabels)
    }

    private fun logChange(showLabels: Boolean) {
+6 −10
Original line number Diff line number Diff line
@@ -14,22 +14,18 @@
 * limitations under the License.
 */

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

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.panels.data.repository.QSPreferencesRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.Flow

/** Repository for whether to show the labels of icon tiles. */
@SysUISingleton
class IconLabelVisibilityRepository @Inject constructor() {
    // TODO(b/341735914): Persist and back up showLabels
    private val _showLabels = MutableStateFlow(false)
    val showLabels: StateFlow<Boolean> = _showLabels.asStateFlow()
class QSPreferencesInteractor @Inject constructor(private val repo: QSPreferencesRepository) {
    val showLabels: Flow<Boolean> = repo.showLabels

    fun setShowLabels(showLabels: Boolean) {
        _showLabels.value = showLabels
        repo.setShowLabels(showLabels)
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -30,7 +30,9 @@ interface IconLabelVisibilityViewModel {
@SysUISingleton
class IconLabelVisibilityViewModelImpl
@Inject
constructor(private val interactor: IconLabelVisibilityInteractor) : IconLabelVisibilityViewModel {
constructor(
    private val interactor: IconLabelVisibilityInteractor,
) : IconLabelVisibilityViewModel {
    override val showLabels: StateFlow<Boolean> = interactor.showLabels

    override fun setShowLabels(showLabels: Boolean) {
+130 −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.data

import android.content.Context
import android.content.SharedPreferences
import android.content.pm.UserInfo
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.panels.data.repository.QSPreferencesRepository
import com.android.systemui.qs.panels.data.repository.qsPreferencesRepository
import com.android.systemui.settings.userFileManager
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.user.data.repository.userRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidTestingRunner::class)
class QSPreferencesRepositoryTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val underTest = with(kosmos) { qsPreferencesRepository }

    @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(
                QSPreferencesRepository.FILE_NAME,
                Context.MODE_PRIVATE,
                userRepository.getSelectedUserInfo().id,
            )
        }

    private fun setShowLabelsInSharedPreferences(value: Boolean) {
        getSharedPreferences().edit().putBoolean(ICON_LABELS_KEY, value).apply()
    }

    private fun getShowLabelsFromSharedPreferences(defaultValue: Boolean): Boolean {
        return getSharedPreferences().getBoolean(ICON_LABELS_KEY, defaultValue)
    }

    companion object {
        private const val ICON_LABELS_KEY = "show_icon_labels"
        private const val PRIMARY_USER_ID = 0
        private val PRIMARY_USER = UserInfo(PRIMARY_USER_ID, "user 0", UserInfo.FLAG_MAIN)
        private const val ANOTHER_USER_ID = 1
        private val ANOTHER_USER = UserInfo(ANOTHER_USER_ID, "user 1", UserInfo.FLAG_FULL)
        private val USERS = listOf(PRIMARY_USER, ANOTHER_USER)
    }
}
Loading