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

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

Merge "Separate the normal and edit versions of tile composables" into main

parents d4094185 fd6c7736
Loading
Loading
Loading
Loading
+0 −183
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.interactor

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
import com.android.systemui.qs.panels.data.repository.gridLayoutTypeRepository
import com.android.systemui.qs.panels.shared.model.GridLayoutType
import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class GridConsistencyInteractorTest : SysuiTestCase() {

    data object NoopGridLayoutType : GridLayoutType

    private val kosmos =
        testKosmos().apply {
            defaultLargeTilesRepository =
                object : DefaultLargeTilesRepository {
                    override val defaultLargeTiles =
                        setOf(
                            TileSpec.create("largeA"),
                            TileSpec.create("largeB"),
                            TileSpec.create("largeC"),
                            TileSpec.create("largeD"),
                        )
                }
            gridConsistencyInteractorsMap =
                mapOf(
                    Pair(NoopGridLayoutType, noopGridConsistencyInteractor),
                    Pair(InfiniteGridLayoutType, infiniteGridConsistencyInteractor)
                )
        }

    private val underTest = with(kosmos) { gridConsistencyInteractor }

    @Before
    fun setUp() {
        // Mostly testing InfiniteGridConsistencyInteractor because it reorders tiles
        with(kosmos) { gridLayoutTypeRepository.setLayout(InfiniteGridLayoutType) }
        underTest.start()
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    @Test
    fun changeLayoutType_usesCorrectGridConsistencyInteractor() =
        with(kosmos) {
            testScope.runTest {
                // Using the no-op grid consistency interactor
                gridLayoutTypeRepository.setLayout(NoopGridLayoutType)

                // Setting an invalid layout with holes
                // [ Large A ] [ sa ]
                // [ Large B ] [ Large C ]
                // [ sb ] [ Large D ]
                val newTiles =
                    listOf(
                        TileSpec.create("largeA"),
                        TileSpec.create("smallA"),
                        TileSpec.create("largeB"),
                        TileSpec.create("largeC"),
                        TileSpec.create("smallB"),
                        TileSpec.create("largeD"),
                    )
                tileSpecRepository.setTiles(0, newTiles)

                runCurrent()

                val tiles = currentTilesInteractor.currentTiles.value
                val tileSpecs = tiles.map { it.spec }

                // Saved tiles should be unchanged
                assertThat(tileSpecs).isEqualTo(newTiles)
            }
        }

    @Test
    fun validTilesWithInfiniteGridConsistencyInteractor_unchangedList() =
        with(kosmos) {
            testScope.runTest {
                // Setting a valid layout with holes
                // [ Large A ] [ sa ][ sb ]
                // [ Large B ] [ Large C ]
                // [ Large D ]
                val newTiles =
                    listOf(
                        TileSpec.create("largeA"),
                        TileSpec.create("smallA"),
                        TileSpec.create("smallB"),
                        TileSpec.create("largeB"),
                        TileSpec.create("largeC"),
                        TileSpec.create("largeD"),
                    )
                tileSpecRepository.setTiles(0, newTiles)

                runCurrent()

                val tiles = currentTilesInteractor.currentTiles.value
                val tileSpecs = tiles.map { it.spec }

                // Saved tiles should be unchanged
                assertThat(tileSpecs).isEqualTo(newTiles)
            }
        }

    @Test
    fun invalidTilesWithInfiniteGridConsistencyInteractor_savesNewList() =
        with(kosmos) {
            testScope.runTest {
                // Setting an invalid layout with holes
                // [ sa ] [ Large A ]
                // [ Large B ] [ sb ] [ sc ]
                // [ sd ] [ se ] [ Large C ]
                val newTiles =
                    listOf(
                        TileSpec.create("smallA"),
                        TileSpec.create("largeA"),
                        TileSpec.create("largeB"),
                        TileSpec.create("smallB"),
                        TileSpec.create("smallC"),
                        TileSpec.create("smallD"),
                        TileSpec.create("smallE"),
                        TileSpec.create("largeC"),
                    )
                tileSpecRepository.setTiles(0, newTiles)

                runCurrent()

                val tiles = currentTilesInteractor.currentTiles.value
                val tileSpecs = tiles.map { it.spec }

                // Expected grid
                // [ sa ] [ Large A ] [ sb ]
                // [ Large B ] [ sc ] [ sd ]
                // [ se ] [ Large C ]
                val expectedTiles =
                    listOf(
                        TileSpec.create("smallA"),
                        TileSpec.create("largeA"),
                        TileSpec.create("smallB"),
                        TileSpec.create("largeB"),
                        TileSpec.create("smallC"),
                        TileSpec.create("smallD"),
                        TileSpec.create("smallE"),
                        TileSpec.create("largeC"),
                    )

                // Saved tiles should be unchanged
                assertThat(tileSpecs).isEqualTo(expectedTiles)
            }
        }
}
+0 −187
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.interactor

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class InfiniteGridConsistencyInteractorTest : SysuiTestCase() {

    private val kosmos =
        testKosmos().apply {
            defaultLargeTilesRepository =
                object : DefaultLargeTilesRepository {
                    override val defaultLargeTiles: Set<TileSpec> =
                        setOf(
                            TileSpec.create("largeA"),
                            TileSpec.create("largeB"),
                            TileSpec.create("largeC"),
                            TileSpec.create("largeD"),
                        )
                }
        }
    private val underTest = with(kosmos) { infiniteGridConsistencyInteractor }

    @Test
    fun validTiles_returnsUnchangedList() =
        with(kosmos) {
            testScope.runTest {
                // Original grid
                // [ Large A ] [ sa ][ sb ]
                // [ Large B ] [ Large C ]
                // [ Large D ]
                val tiles =
                    listOf(
                        TileSpec.create("largeA"),
                        TileSpec.create("smallA"),
                        TileSpec.create("smallB"),
                        TileSpec.create("largeB"),
                        TileSpec.create("largeC"),
                        TileSpec.create("largeD"),
                    )

                val newTiles = underTest.reconcileTiles(tiles)

                assertThat(newTiles).isEqualTo(tiles)
            }
        }

    @Test
    fun invalidTiles_moveIconTileForward() =
        with(kosmos) {
            testScope.runTest {
                // Original grid
                // [ Large A ] [ sa ]
                // [ Large B ] [ Large C ]
                // [ sb ] [ Large D ]
                val tiles =
                    listOf(
                        TileSpec.create("largeA"),
                        TileSpec.create("smallA"),
                        TileSpec.create("largeB"),
                        TileSpec.create("largeC"),
                        TileSpec.create("smallB"),
                        TileSpec.create("largeD"),
                    )
                // Expected grid
                // [ Large A ] [ sa ][ sb ]
                // [ Large B ] [ Large C ]
                // [ Large D ]
                val expectedTiles =
                    listOf(
                        TileSpec.create("largeA"),
                        TileSpec.create("smallA"),
                        TileSpec.create("smallB"),
                        TileSpec.create("largeB"),
                        TileSpec.create("largeC"),
                        TileSpec.create("largeD"),
                    )

                val newTiles = underTest.reconcileTiles(tiles)

                assertThat(newTiles).isEqualTo(expectedTiles)
            }
        }

    @Test
    fun invalidTiles_moveIconTileBack() =
        with(kosmos) {
            testScope.runTest {
                // Original grid
                // [ sa ] [ Large A ]
                // [ Large B ] [ Large C ]
                // [ Large D ]
                val tiles =
                    listOf(
                        TileSpec.create("smallA"),
                        TileSpec.create("largeA"),
                        TileSpec.create("largeB"),
                        TileSpec.create("largeC"),
                        TileSpec.create("largeD"),
                    )
                // Expected grid
                // [ Large A ] [ Large B ]
                // [ Large C ] [ Large D ]
                // [ sa ]
                val expectedTiles =
                    listOf(
                        TileSpec.create("largeA"),
                        TileSpec.create("largeB"),
                        TileSpec.create("largeC"),
                        TileSpec.create("largeD"),
                        TileSpec.create("smallA"),
                    )

                val newTiles = underTest.reconcileTiles(tiles)

                assertThat(newTiles).isEqualTo(expectedTiles)
            }
        }

    @Test
    fun invalidTiles_multipleCorrections() =
        with(kosmos) {
            testScope.runTest {
                // Original grid
                // [ sa ] [ Large A ]
                // [ Large B ] [ sb ] [ sc ]
                // [ sd ] [ se ] [ Large C ]
                val tiles =
                    listOf(
                        TileSpec.create("smallA"),
                        TileSpec.create("largeA"),
                        TileSpec.create("largeB"),
                        TileSpec.create("smallB"),
                        TileSpec.create("smallC"),
                        TileSpec.create("smallD"),
                        TileSpec.create("smallE"),
                        TileSpec.create("largeC"),
                    )
                // Expected grid
                // [ sa ] [ Large A ] [ sb ]
                // [ Large B ] [ sc ] [ sd ]
                // [ se ] [ Large C ]
                val expectedTiles =
                    listOf(
                        TileSpec.create("smallA"),
                        TileSpec.create("largeA"),
                        TileSpec.create("smallB"),
                        TileSpec.create("largeB"),
                        TileSpec.create("smallC"),
                        TileSpec.create("smallD"),
                        TileSpec.create("smallE"),
                        TileSpec.create("largeC"),
                    )

                val newTiles = underTest.reconcileTiles(tiles)

                assertThat(newTiles).isEqualTo(expectedTiles)
            }
        }
}
+3 −7
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
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.panels.ui.viewmodel.fixedColumnsSizeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel
@@ -44,12 +45,7 @@ class InfiniteGridLayoutTest : SysuiTestCase() {
        }

    private val underTest =
        with(kosmos) {
            InfiniteGridLayout(
                iconTilesViewModel,
                fixedColumnsSizeViewModel,
            )
        }
        with(kosmos) { InfiniteGridLayout(iconTilesViewModel, fixedColumnsSizeViewModel) }

    @Test
    fun correctPagination_underOnePage_sameOrder() =
@@ -65,7 +61,7 @@ class InfiniteGridLayoutTest : SysuiTestCase() {
                        smallTile(),
                        largeTile(),
                        largeTile(),
                        smallTile()
                        smallTile(),
                    )

                val pages = underTest.splitIntoPages(tiles, rows = rows, columns = columns)
+1 −38
Original line number Diff line number Diff line
@@ -23,17 +23,14 @@ import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepositor
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepositoryImpl
import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepositoryImpl
import com.android.systemui.qs.panels.domain.interactor.GridTypeConsistencyInteractor
import com.android.systemui.qs.panels.domain.interactor.InfiniteGridConsistencyInteractor
import com.android.systemui.qs.panels.domain.interactor.NoopGridConsistencyInteractor
import com.android.systemui.qs.panels.shared.model.GridLayoutType
import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
import com.android.systemui.qs.panels.shared.model.PanelsLog
import com.android.systemui.qs.panels.ui.compose.GridLayout
import com.android.systemui.qs.panels.ui.compose.InfiniteGridLayout
import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout
import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout
import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModelImpl
import com.android.systemui.qs.panels.ui.viewmodel.IconLabelVisibilityViewModel
@@ -56,11 +53,6 @@ interface PanelsModule {
    @Binds
    fun bindGridLayoutTypeRepository(impl: GridLayoutTypeRepositoryImpl): GridLayoutTypeRepository

    @Binds
    fun bindDefaultGridConsistencyInteractor(
        impl: NoopGridConsistencyInteractor
    ): GridTypeConsistencyInteractor

    @Binds fun bindIconTilesViewModel(impl: IconTilesViewModelImpl): IconTilesViewModel

    @Binds fun bindGridSizeViewModel(impl: FixedColumnsSizeViewModelImpl): FixedColumnsSizeViewModel
@@ -74,12 +66,6 @@ interface PanelsModule {
    @PaginatedBaseLayoutType
    fun bindPaginatedBaseGridLayout(impl: InfiniteGridLayout): PaginatableGridLayout

    @Binds
    @PaginatedBaseLayoutType
    fun bindPaginatedBaseConsistencyInteractor(
        impl: NoopGridConsistencyInteractor
    ): GridTypeConsistencyInteractor

    @Binds @Named("Default") fun bindDefaultGridLayout(impl: PaginatedGridLayout): GridLayout

    companion object {
@@ -117,28 +103,5 @@ interface PanelsModule {
        ): Set<GridLayoutType> {
            return entries.map { it.first }.toSet()
        }

        @Provides
        @IntoSet
        fun provideGridConsistencyInteractor(
            consistencyInteractor: InfiniteGridConsistencyInteractor
        ): Pair<GridLayoutType, GridTypeConsistencyInteractor> {
            return Pair(InfiniteGridLayoutType, consistencyInteractor)
        }

        @Provides
        @IntoSet
        fun providePaginatedGridConsistencyInteractor(
            @PaginatedBaseLayoutType consistencyInteractor: GridTypeConsistencyInteractor,
        ): Pair<GridLayoutType, GridTypeConsistencyInteractor> {
            return Pair(PaginatedGridLayoutType, consistencyInteractor)
        }

        @Provides
        fun provideGridConsistencyInteractorMap(
            entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridTypeConsistencyInteractor>>
        ): Map<GridLayoutType, GridTypeConsistencyInteractor> {
            return entries.toMap()
        }
    }
}
+0 −75
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.interactor

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.shared.model.GridLayoutType
import com.android.systemui.qs.panels.shared.model.PanelsLog
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

@SysUISingleton
class GridConsistencyInteractor
@Inject
constructor(
    private val gridLayoutTypeInteractor: GridLayoutTypeInteractor,
    private val currentTilesInteractor: CurrentTilesInteractor,
    private val consistencyInteractors:
        Map<GridLayoutType, @JvmSuppressWildcards GridTypeConsistencyInteractor>,
    private val defaultConsistencyInteractor: GridTypeConsistencyInteractor,
    @PanelsLog private val logBuffer: LogBuffer,
    @Application private val applicationScope: CoroutineScope,
) {
    fun start() {
        applicationScope.launch {
            gridLayoutTypeInteractor.layout.collectLatest { type ->
                val consistencyInteractor =
                    consistencyInteractors[type] ?: defaultConsistencyInteractor
                currentTilesInteractor.currentTiles
                    .map { tiles -> tiles.map { it.spec } }
                    .collectLatest { tiles ->
                        val newTiles = consistencyInteractor.reconcileTiles(tiles)
                        if (newTiles != tiles) {
                            currentTilesInteractor.setTiles(newTiles)
                            logChange(newTiles)
                        }
                    }
            }
        }
    }

    private fun logChange(tiles: List<TileSpec>) {
        logBuffer.log(
            LOG_BUFFER_CURRENT_TILES_CHANGE_TAG,
            LogLevel.DEBUG,
            { str1 = tiles.toString() },
            { "Tiles reordered: $str1" }
        )
    }

    private companion object {
        const val LOG_BUFFER_CURRENT_TILES_CHANGE_TAG = "GridConsistencyTilesChange"
    }
}
Loading