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

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

Merge "Fix concurrency error when resetting tiles and sizes at the same time" into main

parents 324122b4 ddfc6fd8
Loading
Loading
Loading
Loading
+9 −0
Original line number Original line Diff line number Diff line
@@ -85,6 +85,15 @@ class QSPreferencesRepositoryTest : SysuiTestCase() {
        assertThat(getLargeTilesSpecsFromSharedPreferences()).isEqualTo(setB)
        assertThat(getLargeTilesSpecsFromSharedPreferences()).isEqualTo(setB)
    }
    }


    @Test
    fun removeLargeTilesSpecs_inSharedPreferences() {
        val setA = setOf("tileA", "tileB", "tileC", "tileD")
        setLargeTilesSpecsInSharedPreferences(setA)

        underTest.removeLargeTileSpecs(setOf(TileSpec.create("tileB"), TileSpec.create("tileD")))
        assertThat(getLargeTilesSpecsFromSharedPreferences()).isEqualTo(setOf("tileA", "tileC"))
    }

    @Test
    @Test
    fun setLargeTilesSpecs_forDifferentUser() =
    fun setLargeTilesSpecs_forDifferentUser() =
        with(kosmos) {
        with(kosmos) {
+21 −0
Original line number Original line Diff line number Diff line
@@ -29,12 +29,14 @@ import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.testKosmos
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Before
import org.junit.Test
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runner.RunWith


@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWith(AndroidJUnit4::class)
class DynamicIconTilesInteractorTest : SysuiTestCase() {
class DynamicIconTilesInteractorTest : SysuiTestCase() {
@@ -72,8 +74,27 @@ class DynamicIconTilesInteractorTest : SysuiTestCase() {
            }
            }
        }
        }


    @Test
    fun removingAndResizingTiles_updatesSharedPreferences() =
        with(kosmos) {
            testScope.runTest {
                val latest by collectLastValue(qsPreferencesRepository.largeTilesSpecs)
                runCurrent()

                // Remove the large tile from the current tiles
                iconTilesInteractor.setLargeTiles(latest!! + newTile)
                currentTilesInteractor.removeTiles(listOf(largeTile))
                runCurrent()

                // Assert that it resized to small
                assertThat(latest).doesNotContain(largeTile)
                assertThat(latest).contains(newTile)
            }
        }

    private companion object {
    private companion object {
        private val largeTile = TileSpec.create("large")
        private val largeTile = TileSpec.create("large")
        private val smallTile = TileSpec.create("small")
        private val smallTile = TileSpec.create("small")
        private val newTile = TileSpec.create("new")
    }
    }
}
}
+19 −9
Original line number Original line Diff line number Diff line
@@ -71,15 +71,7 @@ constructor(
        combine(backupRestorationEvents, userRepository.selectedUserInfo, ::Pair)
        combine(backupRestorationEvents, userRepository.selectedUserInfo, ::Pair)
            .flatMapLatest { (_, userInfo) ->
            .flatMapLatest { (_, userInfo) ->
                val prefs = getSharedPrefs(userInfo.id)
                val prefs = getSharedPrefs(userInfo.id)
                prefs.observe().emitOnStart().map {
                prefs.observe().emitOnStart().map { prefs.getLargeTilesSpecs() }
                    prefs
                        .getStringSet(
                            LARGE_TILES_SPECS_KEY,
                            defaultLargeTilesRepository.defaultLargeTiles.map { it.spec }.toSet(),
                        )
                        ?.map { TileSpec.create(it) }
                        ?.toSet() ?: defaultLargeTilesRepository.defaultLargeTiles
                }
            }
            }
            .flowOn(backgroundDispatcher)
            .flowOn(backgroundDispatcher)


@@ -91,6 +83,15 @@ constructor(
        }
        }
    }
    }


    /** Remove the set of [TileSpec] from the current large tiles. */
    fun removeLargeTileSpecs(specs: Set<TileSpec>) {
        with(getSharedPrefs(userRepository.getSelectedUserInfo().id)) {
            val largeSpecs = getLargeTilesSpecs()
            writeLargeTileSpecs(largeSpecs - specs)
            setLargeTilesDefault(false)
        }
    }

    suspend fun deleteLargeTileDataJob() {
    suspend fun deleteLargeTileDataJob() {
        userRepository.selectedUserInfo.collect { userInfo ->
        userRepository.selectedUserInfo.collect { userInfo ->
            getSharedPrefs(userInfo.id)
            getSharedPrefs(userInfo.id)
@@ -105,6 +106,15 @@ constructor(
        edit().putStringSet(LARGE_TILES_SPECS_KEY, specs.map { it.spec }.toSet()).apply()
        edit().putStringSet(LARGE_TILES_SPECS_KEY, specs.map { it.spec }.toSet()).apply()
    }
    }


    private fun SharedPreferences.getLargeTilesSpecs(): Set<TileSpec> {
        return getStringSet(
                LARGE_TILES_SPECS_KEY,
                defaultLargeTilesRepository.defaultLargeTiles.map { it.spec }.toSet(),
            )
            ?.map { TileSpec.create(it) }
            ?.toSet() ?: defaultLargeTilesRepository.defaultLargeTiles
    }

    /**
    /**
     * Sets the initial set of large tiles. One of the following cases will happen:
     * Sets the initial set of large tiles. One of the following cases will happen:
     * * If we are setting the default set (no value stored in settings for the list of tiles), set
     * * If we are setting the default set (no value stored in settings for the list of tiles), set
+36 −8
Original line number Original line Diff line number Diff line
@@ -17,9 +17,18 @@
package com.android.systemui.qs.panels.domain.interactor
package com.android.systemui.qs.panels.domain.interactor


import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.qs.panels.shared.model.PanelsLog
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.util.kotlin.pairwise
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach


/** Interactor to resize QS tiles down to icons when removed from the current tiles. */
/** Interactor to resize QS tiles down to icons when removed from the current tiles. */
class DynamicIconTilesInteractor
class DynamicIconTilesInteractor
@@ -27,21 +36,40 @@ class DynamicIconTilesInteractor
constructor(
constructor(
    private val iconTilesInteractor: IconTilesInteractor,
    private val iconTilesInteractor: IconTilesInteractor,
    private val currentTilesInteractor: CurrentTilesInteractor,
    private val currentTilesInteractor: CurrentTilesInteractor,
    @PanelsLog private val logBuffer: LogBuffer,
) : ExclusiveActivatable() {
) : ExclusiveActivatable() {


    override suspend fun onActivated(): Nothing {
    override suspend fun onActivated(): Nothing {
        currentTilesInteractor.currentTiles.collect { currentTiles ->
        currentTilesInteractor.userAndTiles
            // Only current tiles can be resized, so observe the current tiles and find the
            .pairwise()
            // intersection between them and the large tiles.
            .filter { !it.newValue.userChange } // Only compare tile changes for the same user
            val newLargeTiles =
            .map { tilesData ->
                iconTilesInteractor.largeTilesSpecs.value intersect
                // Only current tiles can be resized, so find removed tiles before updating their
                    currentTiles.map { it.spec }.toSet()
                // sizes
            iconTilesInteractor.setLargeTiles(newLargeTiles)
                val removedTiles = tilesData.previousValue.tiles - tilesData.newValue.tiles.toSet()
                removedTiles.toSet()
            }
            }
            .filter { it.isNotEmpty() }
            .onEach { logChange(it) }
            .collect { removedTiles -> iconTilesInteractor.removeLargeTiles(removedTiles) }
        awaitCancellation()
    }
    }


    @AssistedFactory
    @AssistedFactory
    interface Factory {
    interface Factory {
        fun create(): DynamicIconTilesInteractor
        fun create(): DynamicIconTilesInteractor
    }
    }

    private fun logChange(deletedSpecs: Set<TileSpec>) {
        logBuffer.log(
            LOG_BUFFER_DELETED_TILES_RESIZED_TAG,
            LogLevel.DEBUG,
            { str1 = deletedSpecs.toString() },
            { "Removed tiles=$str1" },
        )
    }

    private companion object {
        const val LOG_BUFFER_DELETED_TILES_RESIZED_TAG = "RemovedTiles"
    }
}
}
+6 −0
Original line number Original line Diff line number Diff line
@@ -58,10 +58,16 @@ constructor(


    fun isIconTile(spec: TileSpec): Boolean = !largeTilesSpecs.value.contains(spec)
    fun isIconTile(spec: TileSpec): Boolean = !largeTilesSpecs.value.contains(spec)


    /** Set the large tiles to be [specs] */
    fun setLargeTiles(specs: Set<TileSpec>) {
    fun setLargeTiles(specs: Set<TileSpec>) {
        preferencesInteractor.setLargeTilesSpecs(specs)
        preferencesInteractor.setLargeTilesSpecs(specs)
    }
    }


    /** Remove [specs] from the current set of large tiles */
    fun removeLargeTiles(specs: Set<TileSpec>) {
        preferencesInteractor.removeLargeTilesSpecs(specs)
    }

    fun resetToDefault() {
    fun resetToDefault() {
        preferencesInteractor.setLargeTilesSpecs(repo.defaultLargeTiles)
        preferencesInteractor.setLargeTilesSpecs(repo.defaultLargeTiles)
    }
    }
Loading