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

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

Implement stretch option for QS tiles

Rows with holes with stretch tiles to fill in empty spaces (except last row).
Edit mode reuses the same composable as InfiniteGridLayout, and the grid consistency interactor doesn't do any reordering.
This change also adds a layout selector to swap between prototypes.

Fix: 340246005
Flag: ACONFIG com.android.systemui.qs_ui_refactor DEVELOPMENT
Test: manually using layout selector on QSActivity

Change-Id: Ie4dbb81f99e3e6f0c00233974d518ec8b8612282
parent b310a0e0
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -29,8 +29,10 @@ import com.android.systemui.qs.panels.domain.interactor.NoopGridConsistencyInter
import com.android.systemui.qs.panels.shared.model.GridConsistencyLog
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.StretchedGridLayoutType
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.StretchedGridLayout
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -62,6 +64,14 @@ interface PanelsModule {
            return Pair(InfiniteGridLayoutType, gridLayout)
        }

        @Provides
        @IntoSet
        fun provideStretchedGridLayout(
            gridLayout: StretchedGridLayout
        ): Pair<GridLayoutType, GridLayout> {
            return Pair(StretchedGridLayoutType, gridLayout)
        }

        @Provides
        fun provideGridLayoutMap(
            entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridLayout>>
@@ -69,6 +79,13 @@ interface PanelsModule {
            return entries.toMap()
        }

        @Provides
        fun provideGridLayoutTypes(
            entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridLayout>>
        ): Set<GridLayoutType> {
            return entries.map { it.first }.toSet()
        }

        @Provides
        @IntoSet
        fun provideGridConsistencyInteractor(
@@ -77,6 +94,14 @@ interface PanelsModule {
            return Pair(InfiniteGridLayoutType, consistencyInteractor)
        }

        @Provides
        @IntoSet
        fun provideStretchedGridConsistencyInteractor(
            consistencyInteractor: NoopGridConsistencyInteractor
        ): Pair<GridLayoutType, GridTypeConsistencyInteractor> {
            return Pair(StretchedGridLayoutType, consistencyInteractor)
        }

        @Provides
        fun provideGridConsistencyInteractorMap(
            entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridTypeConsistencyInteractor>>
+7 −0
Original line number Diff line number Diff line
@@ -26,10 +26,17 @@ import kotlinx.coroutines.flow.asStateFlow

interface GridLayoutTypeRepository {
    val layout: StateFlow<GridLayoutType>
    fun setLayout(type: GridLayoutType)
}

@SysUISingleton
class GridLayoutTypeRepositoryImpl @Inject constructor() : GridLayoutTypeRepository {
    private val _layout: MutableStateFlow<GridLayoutType> = MutableStateFlow(InfiniteGridLayoutType)
    override val layout = _layout.asStateFlow()

    override fun setLayout(type: GridLayoutType) {
        if (_layout.value != type) {
            _layout.value = type
        }
    }
}
+7 −3
Original line number Diff line number Diff line
@@ -20,9 +20,13 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
import com.android.systemui.qs.panels.shared.model.GridLayoutType
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow

@SysUISingleton
class GridLayoutTypeInteractor @Inject constructor(repo: GridLayoutTypeRepository) {
    val layout: Flow<GridLayoutType> = repo.layout
class GridLayoutTypeInteractor @Inject constructor(private val repo: GridLayoutTypeRepository) {
    val layout: StateFlow<GridLayoutType> = repo.layout

    fun setLayoutType(type: GridLayoutType) {
        repo.setLayout(type)
    }
}
+8 −43
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.systemui.qs.panels.domain.interactor

import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.TileRow
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject

@@ -35,7 +37,7 @@ constructor(
     */
    override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> {
        val newTiles: MutableList<TileSpec> = mutableListOf()
        val row = TileRow(columns = gridSizeInteractor.columns.value)
        val row = TileRow<TileSpec>(columns = gridSizeInteractor.columns.value)
        val iconTilesSet = iconTilesInteractor.iconTilesSpecs.value
        val tilesQueue =
            ArrayDeque(
@@ -54,7 +56,7 @@ constructor(

        while (tilesQueue.isNotEmpty()) {
            if (row.isFull()) {
                newTiles.addAll(row.tileSpecs())
                newTiles.addAll(row.tiles.map { it.tile })
                row.clear()
            }

@@ -66,13 +68,13 @@ constructor(
                // We'll try to either add an icon tile from the queue to complete the row, or
                // remove an icon tile from the current row to free up space.

                val iconTile: SizedTile? = tilesQueue.firstOrNull { it.width == 1 }
                val iconTile: SizedTile<TileSpec>? = tilesQueue.firstOrNull { it.width == 1 }
                if (iconTile != null) {
                    tilesQueue.remove(iconTile)
                    tilesQueue.addFirst(tile)
                    row.maybeAddTile(iconTile)
                } else {
                    val tileToRemove: SizedTile? = row.findLastIconTile()
                    val tileToRemove: SizedTile<TileSpec>? = row.findLastIconTile()
                    if (tileToRemove != null) {
                        row.removeTile(tileToRemove)
                        row.maybeAddTile(tile)
@@ -84,7 +86,7 @@ constructor(
                        // If the row does not have an icon tile, add the incomplete row.
                        // Note: this shouldn't happen because an icon tile is guaranteed to be in a
                        // row that doesn't have enough space for a large tile.
                        val tileSpecs = row.tileSpecs()
                        val tileSpecs = row.tiles.map { it.tile }
                        Log.wtf(TAG, "Uneven row does not have an icon tile to remove: $tileSpecs")
                        newTiles.addAll(tileSpecs)
                        row.clear()
@@ -95,48 +97,11 @@ constructor(
        }

        // Add last row that might be incomplete
        newTiles.addAll(row.tileSpecs())
        newTiles.addAll(row.tiles.map { it.tile })

        return newTiles.toList()
    }

    /** Tile with a width representing the number of columns it should take. */
    private data class SizedTile(val spec: TileSpec, val width: Int)

    private class TileRow(private val columns: Int) {
        private var availableColumns = columns
        private val tiles: MutableList<SizedTile> = mutableListOf()

        fun tileSpecs(): List<TileSpec> {
            return tiles.map { it.spec }
        }

        fun maybeAddTile(tile: SizedTile): Boolean {
            if (availableColumns - tile.width >= 0) {
                tiles.add(tile)
                availableColumns -= tile.width
                return true
            }
            return false
        }

        fun findLastIconTile(): SizedTile? {
            return tiles.findLast { it.width == 1 }
        }

        fun removeTile(tile: SizedTile) {
            tiles.remove(tile)
            availableColumns += tile.width
        }

        fun clear() {
            tiles.clear()
            availableColumns = columns
        }

        fun isFull(): Boolean = availableColumns == 0
    }

    private companion object {
        const val TAG = "InfiniteGridConsistencyInteractor"
    }
+26 −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.interactor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject

@SysUISingleton
class NoopConsistencyInteractor @Inject constructor() : GridTypeConsistencyInteractor {
    override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> = tiles
}
Loading