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

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

Move the large tile span logic to the columns viewmodel

The columns viewmodel might change the actual number of columns used, which is needed when calculating the span on extra large tiles
In order to use this value from the InfiniteGridLayout while keeping it out of PaginatableGridLayout, I added a PaginatableViewModel interface.

Flag: com.android.systemui.qs_ui_refactor_compose_fragment
Test: QSColumnsViewModelTest
Test: InfiniteGridViewModelTest
Bug: 424081859
Change-Id: I582cc28b73353f3440585f3ab96f9812d0dc4abe
parent e8532f98
Loading
Loading
Loading
Loading
+0 −147
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.interactor

import android.content.res.Configuration
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
class LargeTileSpanInteractorTest : SysuiTestCase() {
    private val kosmos =
        testKosmos().useUnconfinedTestDispatcher().apply {
            testCase.context.orCreateTestableResources.addOverride(
                R.integer.quick_settings_infinite_grid_num_columns,
                4,
            )
            testCase.context.orCreateTestableResources.addOverride(
                R.integer.quick_settings_infinite_grid_tile_max_width,
                4,
            )
        }
    private val Kosmos.underTest by Kosmos.Fixture { largeTileSpanInteractor }

    @Test
    fun span_normalTiles_ignoreColumns() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.span)

            // Set extra large tiles to false
            val configuration = Configuration().apply { this.fontScale = 1f }
            context.orCreateTestableResources.overrideConfiguration(configuration)
            fakeConfigurationRepository.onConfigurationChange()

            // Not using extra large tiles means that we stay to the default width of 2, regardless
            // of columns
            assertThat(latest).isEqualTo(2)

            setColumns(10)
            assertThat(latest).isEqualTo(2)
        }

    @Test
    fun span_extraLargeTiles_tracksColumns() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.span)

            // Set extra large tiles to true
            val configuration = Configuration().apply { this.fontScale = 2f }
            context.orCreateTestableResources.overrideConfiguration(configuration)
            fakeConfigurationRepository.onConfigurationChange()

            // Using extra large tiles with a max width of 4 means that we change the width to the
            // same as the columns if equal or under 4, otherwise we divide it in half
            assertThat(latest).isEqualTo(4)

            setColumns(2)
            assertThat(latest).isEqualTo(2)

            setColumns(6)
            assertThat(latest).isEqualTo(3)

            setColumns(8)
            assertThat(latest).isEqualTo(4)
        }

    @Test
    fun span_extraLargeTiles_tracksMaxWidth() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.span)

            // Set extra large tiles to true
            val configuration = Configuration().apply { this.fontScale = 2f }
            context.orCreateTestableResources.overrideConfiguration(configuration)
            fakeConfigurationRepository.onConfigurationChange()

            // Using extra large tiles with 4 columns means that we change the width to be 4, unless
            // we're using a max width lower than 4 in which case divide it in half
            assertThat(latest).isEqualTo(4)

            setMaxWidth(3)
            assertThat(latest).isEqualTo(2)

            setMaxWidth(6)
            assertThat(latest).isEqualTo(4)

            setMaxWidth(8)
            assertThat(latest).isEqualTo(4)
        }

    @Test
    fun span_tracksExtraLargeTiles() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.span)

            // Set extra large tiles to false
            val configuration = Configuration().apply { this.fontScale = 1f }
            context.orCreateTestableResources.overrideConfiguration(configuration)
            fakeConfigurationRepository.onConfigurationChange()

            assertThat(latest).isEqualTo(2)

            // Set extra large tiles to true
            configuration.fontScale = 2f
            fakeConfigurationRepository.onConfigurationChange()
            assertThat(latest).isEqualTo(4)
        }

    private fun setColumns(columns: Int) =
        setValueInConfig(columns, R.integer.quick_settings_infinite_grid_num_columns)

    private fun setMaxWidth(width: Int) =
        setValueInConfig(width, R.integer.quick_settings_infinite_grid_tile_max_width)

    private fun setValueInConfig(value: Int, id: Int) =
        with(kosmos) {
            testCase.context.orCreateTestableResources.addOverride(id, value)
            fakeConfigurationRepository.onConfigurationChange()
        }
}
+212 −0
Original line number Diff line number Diff line
@@ -14,17 +14,24 @@
 * limitations under the License.
 */

package com.android.systemui.qs.panels.ui.compose
package com.android.systemui.qs.panels.ui.viewmodel

import android.content.res.Configuration
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
import com.android.systemui.qs.panels.ui.compose.infinitegrid.infiniteGridLayout
import com.android.systemui.qs.panels.ui.viewmodel.MockTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
@@ -34,23 +41,27 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
class InfiniteGridLayoutTest : SysuiTestCase() {
class InfiniteGridViewModelTest : SysuiTestCase() {
    private val kosmos =
        testKosmos().apply {
        testKosmos().useUnconfinedTestDispatcher().apply {
            testCase.context.orCreateTestableResources.addOverride(
                R.integer.quick_settings_infinite_grid_num_columns,
                4,
            )
            defaultLargeTilesRepository =
                object : DefaultLargeTilesRepository {
                    override val defaultLargeTiles: Set<TileSpec> = setOf(TileSpec.create("large"))
                }
            usingMediaInComposeFragment = false
        }

    private val underTest = kosmos.infiniteGridLayout
    private val Kosmos.underTest by Kosmos.Fixture { infiniteGridLayout.viewModelFactory.create() }

    @Test
    fun correctPagination_underOnePage_sameOrder() =
        with(kosmos) {
            testScope.runTest {
                val rows = 3
                val columns = 4

                val tiles =
                    listOf(
@@ -62,7 +73,7 @@ class InfiniteGridLayoutTest : SysuiTestCase() {
                        smallTile(),
                    )

                val pages = underTest.splitIntoPages(tiles, rows = rows, columns = columns)
                val pages = underTest.splitIntoPages(tiles, rows = rows)

                assertThat(pages).hasSize(1)
                assertThat(pages[0]).isEqualTo(tiles)
@@ -74,7 +85,6 @@ class InfiniteGridLayoutTest : SysuiTestCase() {
        with(kosmos) {
            testScope.runTest {
                val rows = 3
                val columns = 4

                val tiles =
                    listOf(
@@ -99,7 +109,7 @@ class InfiniteGridLayoutTest : SysuiTestCase() {
                // [L L] [S] [S]
                // [L L]

                val pages = underTest.splitIntoPages(tiles, rows = rows, columns = columns)
                val pages = underTest.splitIntoPages(tiles, rows = rows)

                assertThat(pages).hasSize(2)
                assertThat(pages[0]).isEqualTo(tiles.take(8))
@@ -107,6 +117,93 @@ class InfiniteGridLayoutTest : SysuiTestCase() {
            }
        }

    @Test
    fun correctPagination_differentColumns_sameOrder() =
        with(kosmos) {
            testScope.runTest {
                underTest.activateIn(testScope)
                val rows = 3

                // Set columns to 2
                testCase.context.orCreateTestableResources.addOverride(
                    R.integer.quick_settings_infinite_grid_num_columns,
                    2,
                )
                fakeConfigurationRepository.onConfigurationChange()

                val tiles =
                    listOf(
                        largeTile(),
                        smallTile(),
                        smallTile(),
                        largeTile(),
                        largeTile(),
                        smallTile(),
                        smallTile(),
                        largeTile(),
                        largeTile(),
                        smallTile(),
                        smallTile(),
                        largeTile(),
                    )
                // --- Page 1 ---
                // [L L] [S] [S]
                // [L L] [L L]
                // [S] [S] [L L]
                // --- Page 2 ---
                // [L L] [S] [S]
                // [L L]

                val pages = underTest.splitIntoPages(tiles, rows = rows)

                assertThat(pages).hasSize(3)
                assertThat(pages[0]).isEqualTo(tiles.take(4))
                assertThat(pages[1]).isEqualTo(tiles.subList(4, 8))
                assertThat(pages[2]).isEqualTo(tiles.drop(8))
            }
        }

    @Test
    fun correctPagination_extraLargeTiles_sameOrder() =
        with(kosmos) {
            testScope.runTest {
                underTest.activateIn(testScope)
                val rows = 3

                // Enable extra large tiles
                val configuration = Configuration().apply { this.fontScale = 2f }
                context.orCreateTestableResources.overrideConfiguration(configuration)
                fakeConfigurationRepository.onConfigurationChange()

                val tiles =
                    listOf(
                        largeTile(),
                        smallTile(),
                        largeTile(),
                        largeTile(),
                        smallTile(),
                        largeTile(),
                        largeTile(),
                        smallTile(),
                        largeTile(),
                    )
                // --- Page 1 ---
                // [L L] [S] [S]
                // [L L] [L L]
                // [S] [S] [L L]
                // --- Page 2 ---
                // [L L] [S] [S]
                // [L L]

                val pages = underTest.splitIntoPages(tiles, rows = rows)

                assertThat(pages).hasSize(3)
                assertThat(pages[0]).isEqualTo(tiles.take(3))
                assertThat(pages[1]).isEqualTo(tiles.subList(3, 6))
                assertThat(pages[2]).isEqualTo(tiles.drop(6))
            }
        }

    companion object {
        fun largeTile() = MockTileViewModel(TileSpec.create("large"))

+6 −18
Original line number Diff line number Diff line
@@ -14,13 +14,12 @@
 * limitations under the License.
 */

package com.android.systemui.qs.panels.ui.compose
package com.android.systemui.qs.panels.ui.viewmodel

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.viewmodel.MockTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -29,24 +28,18 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
class PaginatableGridLayoutTest : SysuiTestCase() {
class PaginatableViewModelTest : SysuiTestCase() {
    @Test
    fun correctRows_gapsAtEnd() {
        val columns = 6

        val sizedTiles =
            listOf(
                largeTile(),
                extraLargeTile(),
                largeTile(),
                smallTile(),
                largeTile(),
            )
            listOf(largeTile(), extraLargeTile(), largeTile(), smallTile(), largeTile())

        // [L L] [XL XL XL]
        // [L L] [S] [L L]

        val rows = PaginatableGridLayout.splitInRows(sizedTiles, columns)
        val rows = PaginatableViewModel.splitInRows(sizedTiles, columns)

        assertThat(rows).hasSize(2)
        assertThat(rows[0]).isEqualTo(sizedTiles.take(2))
@@ -57,16 +50,11 @@ class PaginatableGridLayoutTest : SysuiTestCase() {
    fun correctRows_fullLastRow_noEmptyRow() {
        val columns = 6

        val sizedTiles =
            listOf(
                largeTile(),
                extraLargeTile(),
                smallTile(),
            )
        val sizedTiles = listOf(largeTile(), extraLargeTile(), smallTile())

        // [L L] [XL XL XL] [S]

        val rows = PaginatableGridLayout.splitInRows(sizedTiles, columns)
        val rows = PaginatableViewModel.splitInRows(sizedTiles, columns)

        assertThat(rows).hasSize(1)
        assertThat(rows[0]).isEqualTo(sizedTiles)
+106 −0
Original line number Diff line number Diff line
@@ -16,11 +16,13 @@

package com.android.systemui.qs.panels.ui.viewmodel

import android.content.res.Configuration
import android.content.res.mainResources
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.configurationRepository
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.runTest
@@ -64,6 +66,10 @@ class QSColumnsViewModelTest : SysuiTestCase() {
                R.integer.quick_settings_split_shade_num_columns,
                SINGLE_SPLIT_SHADE_COLUMNS,
            )
            testCase.context.orCreateTestableResources.addOverride(
                R.integer.quick_settings_infinite_grid_tile_max_width,
                4,
            )
            qsColumnsRepository = QSColumnsRepository(mainResources, configurationRepository)
        }

@@ -184,6 +190,106 @@ class QSColumnsViewModelTest : SysuiTestCase() {
            assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS / 2)
        }

    @Test
    fun largeSpan_normalTiles_ignoreColumns() =
        kosmos.runTest {
            val underTest = qsColumnsViewModelFactory.create(LOCATION_QQS)
            underTest.activateIn(testScope)

            // Set extra large tiles to false
            val configuration = Configuration().apply { this.fontScale = 1f }
            context.orCreateTestableResources.overrideConfiguration(configuration)
            fakeConfigurationRepository.onConfigurationChange()

            // Not using extra large tiles means that we stay to the default width of 2, regardless
            // of columns
            assertThat(underTest.largeSpan).isEqualTo(2)

            setColumns(10)
            assertThat(underTest.largeSpan).isEqualTo(2)
        }

    @Test
    fun largeSpan_extraLargeTiles_tracksColumns() =
        kosmos.runTest {
            val underTest = qsColumnsViewModelFactory.create(LOCATION_QQS)
            underTest.activateIn(testScope)

            // Set extra large tiles to true
            val configuration = Configuration().apply { this.fontScale = 2f }
            context.orCreateTestableResources.overrideConfiguration(configuration)
            fakeConfigurationRepository.onConfigurationChange()

            // Using extra large tiles with a max width of 4 means that we change the width to the
            // same as the columns if equal or under 4, otherwise we divide it in half
            assertThat(underTest.largeSpan).isEqualTo(4)

            setColumns(2)
            assertThat(underTest.largeSpan).isEqualTo(2)

            setColumns(6)
            assertThat(underTest.largeSpan).isEqualTo(3)

            setColumns(8)
            assertThat(underTest.largeSpan).isEqualTo(4)
        }

    @Test
    fun largeSpan_extraLargeTiles_tracksMaxWidth() =
        kosmos.runTest {
            val underTest = qsColumnsViewModelFactory.create(LOCATION_QQS)
            underTest.activateIn(testScope)

            // Set extra large tiles to true
            val configuration = Configuration().apply { this.fontScale = 2f }
            context.orCreateTestableResources.overrideConfiguration(configuration)
            fakeConfigurationRepository.onConfigurationChange()

            // Using extra large tiles with 4 columns means that we change the width to be 4, unless
            // we're using a max width lower than 4 in which case divide it in half
            assertThat(underTest.largeSpan).isEqualTo(4)

            setMaxWidth(3)
            assertThat(underTest.largeSpan).isEqualTo(2)

            setMaxWidth(6)
            assertThat(underTest.largeSpan).isEqualTo(4)

            setMaxWidth(8)
            assertThat(underTest.largeSpan).isEqualTo(4)
        }

    @Test
    fun largeSpan_tracksExtraLargeTiles() =
        kosmos.runTest {
            val underTest = qsColumnsViewModelFactory.create(LOCATION_QQS)
            underTest.activateIn(testScope)

            // Set extra large tiles to false
            val configuration = Configuration().apply { this.fontScale = 1f }
            context.orCreateTestableResources.overrideConfiguration(configuration)
            fakeConfigurationRepository.onConfigurationChange()

            assertThat(underTest.largeSpan).isEqualTo(2)

            // Set extra large tiles to true
            configuration.fontScale = 2f
            fakeConfigurationRepository.onConfigurationChange()
            assertThat(underTest.largeSpan).isEqualTo(4)
        }

    private fun setColumns(columns: Int) =
        setValueInConfig(columns, R.integer.quick_settings_infinite_grid_num_columns)

    private fun setMaxWidth(width: Int) =
        setValueInConfig(width, R.integer.quick_settings_infinite_grid_tile_max_width)

    private fun setValueInConfig(value: Int, id: Int) =
        with(kosmos) {
            testCase.context.orCreateTestableResources.addOverride(id, value)
            fakeConfigurationRepository.onConfigurationChange()
        }

    companion object {
        private const val SINGLE_SPLIT_SHADE_COLUMNS = 4
        private const val DUAL_SHADE_COLUMNS = 2
+0 −4
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@ import com.android.systemui.qs.pipeline.shared.metricSpec
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn

@@ -43,7 +42,6 @@ constructor(
    private val currentTilesInteractor: CurrentTilesInteractor,
    private val preferencesInteractor: QSPreferencesInteractor,
    private val uiEventLogger: UiEventLogger,
    largeTilesSpanInteractor: LargeTileSpanInteractor,
    @PanelsLog private val logBuffer: LogBuffer,
    @Background private val scope: CoroutineScope,
) {
@@ -53,8 +51,6 @@ constructor(
            .onEach { logChange(it) }
            .stateIn(scope, SharingStarted.Eagerly, repo.defaultLargeTiles)

    val largeTilesSpan: StateFlow<Int> = largeTilesSpanInteractor.span

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

    /** Set the large tiles to be [specs] */
Loading