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

Commit 05bb5299 authored by Lucas Silva's avatar Lucas Silva Committed by Android (Google) Code Review
Browse files

Merge changes I6c1d345d,Ie3e42c13 into main

* changes:
  Implement resizing of ongoing content in responsive grid
  Add new spacer content type
parents 2953a34a 901496fd
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -187,6 +187,7 @@ import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.ui.viewmodel.ResizeInfo
import com.android.systemui.communal.ui.viewmodel.ResizeableItemFrameViewModel
import com.android.systemui.communal.util.DensityUtils.Companion.adjustedDp
import com.android.systemui.communal.util.ResizeUtils.resizeOngoingItems
import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.lifecycle.rememberViewModel
@@ -834,8 +835,16 @@ private fun BoxScope.CommunalHubLazyGrid(
        gridState = gridState,
        contentPadding = contentPadding,
    ) { sizeInfo ->
        /** Override spans based on the responsive grid size */
        val finalizedList =
            if (sizeInfo != null) {
                resizeOngoingItems(list, sizeInfo.gridSize.height)
            } else {
                list
            }

        itemsIndexed(
            items = list,
            items = finalizedList,
            key = { _, item -> item.key },
            contentType = { _, item -> item.key },
            span = { _, item -> GridItemSpan(item.getSpanOrMax(sizeInfo?.gridSize?.height)) },
@@ -1193,6 +1202,7 @@ private fun CommunalContent(
        is CommunalContentModel.Smartspace -> SmartspaceContent(interactionHandler, model, modifier)
        is CommunalContentModel.Tutorial -> TutorialContent(modifier)
        is CommunalContentModel.Umo -> Umo(viewModel, sceneScope, modifier)
        is CommunalContentModel.Spacer -> Box(Modifier.fillMaxSize())
    }
}

+222 −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.communal.util

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.util.ResizeUtils.resizeOngoingItems
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock

@SmallTest
@RunWith(AndroidJUnit4::class)
class ResizeUtilsTest : SysuiTestCase() {
    private val mockWidget =
        mock<CommunalContentModel.WidgetContent.Widget> {
            on { size } doReturn CommunalContentSize.Responsive(1)
        }

    @Test
    fun noOngoingContent() {
        val list = listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 2)

        assertThat(resized).containsExactly(mockWidget)
    }

    @Test
    fun singleOngoingContent_singleRowGrid() {
        val list = createOngoingListWithSize(1) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 1)

        assertThat(resized.map { it.size })
            .containsExactly(CommunalContentSize.Responsive(1), mockWidget.size)
            .inOrder()
    }

    @Test
    fun singleOngoingContent_twoRowGrid() {
        val list = createOngoingListWithSize(1) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 2)

        assertThat(resized.map { it.size })
            .containsExactly(CommunalContentSize.Responsive(2), mockWidget.size)
            .inOrder()
    }

    @Test
    fun singleOngoingContent_threeRowGrid() {
        val list = createOngoingListWithSize(1) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 3)

        assertThat(resized.map { it.size })
            .containsExactly(
                CommunalContentSize.Responsive(2),
                CommunalContentSize.Responsive(1),
                mockWidget.size,
            )
            .inOrder()
        // A spacer should be added as the second element to avoid mixing widget content
        // with ongoing content.
        assertThat(resized[1]).isInstanceOf(CommunalContentModel.Spacer::class.java)
    }

    @Test
    fun twoOngoingContent_singleRowGrid() {
        val list = createOngoingListWithSize(2) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 1)

        assertThat(resized.map { it.size })
            .containsExactly(
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                mockWidget.size,
            )
            .inOrder()
    }

    @Test
    fun twoOngoingContent_twoRowGrid() {
        val list = createOngoingListWithSize(2) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 2)

        assertThat(resized.map { it.size })
            .containsExactly(
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                mockWidget.size,
            )
            .inOrder()
    }

    @Test
    fun twoOngoingContent_threeRowGrid() {
        val list = createOngoingListWithSize(2) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 3)

        assertThat(resized.map { it.size })
            .containsExactly(
                CommunalContentSize.Responsive(2),
                CommunalContentSize.Responsive(1),
                mockWidget.size,
            )
            .inOrder()
    }

    @Test
    fun threeOngoingContent_singleRowGrid() {
        val list = createOngoingListWithSize(3) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 1)

        assertThat(resized.map { it.size })
            .containsExactly(
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                mockWidget.size,
            )
            .inOrder()
    }

    @Test
    fun threeOngoingContent_twoRowGrid() {
        val list = createOngoingListWithSize(3) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 2)

        assertThat(resized.map { it.size })
            .containsExactly(
                CommunalContentSize.Responsive(2),
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                mockWidget.size,
            )
            .inOrder()
    }

    @Test
    fun threeOngoingContent_threeRowGrid() {
        val list = createOngoingListWithSize(3) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 3)

        assertThat(resized.map { it.size })
            .containsExactly(
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                mockWidget.size,
            )
            .inOrder()
    }

    @Test
    fun fourOngoingContent_singleRowGrid() {
        val list = createOngoingListWithSize(4) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 1)

        assertThat(resized.map { it.size })
            .containsExactly(
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                mockWidget.size,
            )
            .inOrder()
    }

    @Test
    fun fourOngoingContent_twoRowGrid() {
        val list = createOngoingListWithSize(4) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 2)

        assertThat(resized.map { it.size })
            .containsExactly(
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(1),
                mockWidget.size,
            )
            .inOrder()
    }

    @Test
    fun fourOngoingContent_threeRowGrid() {
        val list = createOngoingListWithSize(4) + listOf(mockWidget)
        val resized = resizeOngoingItems(list = list, numRows = 3)

        assertThat(resized.map { it.size })
            .containsExactly(
                CommunalContentSize.Responsive(2),
                CommunalContentSize.Responsive(1),
                CommunalContentSize.Responsive(2),
                CommunalContentSize.Responsive(1),
                mockWidget.size,
            )
            .inOrder()
    }

    private fun createOngoingListWithSize(size: Int): List<CommunalContentModel.Ongoing> {
        return List(size) { CommunalContentModel.Umo(createdTimestampMillis = 100) }
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
import com.android.systemui.Flags.communalResponsiveGrid
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalSmartspaceRepository
@@ -535,7 +536,9 @@ constructor(
                // Order by creation time descending.
                ongoingContent.sortByDescending { it.createdTimestampMillis }
                // Resize the items.
                if (!communalResponsiveGrid()) {
                    ongoingContent.resizeItems()
                }

                // Return the sorted and resized items.
                ongoingContent
+9 −0
Original line number Diff line number Diff line
@@ -110,6 +110,11 @@ sealed interface CommunalContentModel {
            get() = fixedHalfOrResponsiveSize()
    }

    /** An empty spacer to reserve space in the grid. */
    data class Spacer(override val size: CommunalContentSize) : CommunalContentModel {
        override val key: String = KEY.spacer()
    }

    /** A CTA tile in the glanceable hub view mode which can be dismissed. */
    class CtaTileInViewMode : CommunalContentModel {
        override val key: String = KEY.CTA_TILE_IN_VIEW_MODE_KEY
@@ -171,6 +176,10 @@ sealed interface CommunalContentModel {
            fun umo(): String {
                return "umo"
            }

            fun spacer(): String {
                return "spacer_${UUID.randomUUID()}"
            }
        }
    }

+71 −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.communal.util

import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize

object ResizeUtils {
    /**
     * Resizes ongoing items such that we don't mix regular content with ongoing content.
     *
     * NOTE: This is *NOT* a pure function, as it modifies items in the input list.
     *
     * Assumptions:
     * 1. Ongoing content is always at the start of the list.
     * 2. The maximum size of ongoing content is 2 rows.
     */
    fun resizeOngoingItems(
        list: List<CommunalContentModel>,
        numRows: Int,
    ): List<CommunalContentModel> {
        val finalizedList = mutableListOf<CommunalContentModel>()
        val numOngoing = list.count { it is CommunalContentModel.Ongoing }
        // Calculate the number of extra rows we have if each ongoing item were to take up a single
        // row. This is the number of rows we have to distribute across items.
        var extraRows =
            if (numOngoing % numRows == 0) {
                0
            } else {
                numRows - (numOngoing % numRows)
            }
        var remainingRows = numRows

        for (item in list) {
            if (item is CommunalContentModel.Ongoing) {
                if (remainingRows == 0) {
                    // Start a new column.
                    remainingRows = numRows
                }
                val newSize = if (extraRows > 0 && remainingRows > 1) 2 else 1
                item.size = CommunalContentSize.Responsive(newSize)
                finalizedList.add(item)
                extraRows -= (newSize - 1)
                remainingRows -= newSize
            } else {
                if (numOngoing > 0 && remainingRows > 0) {
                    finalizedList.add(
                        CommunalContentModel.Spacer(CommunalContentSize.Responsive(remainingRows))
                    )
                }
                remainingRows = -1
                finalizedList.add(item)
            }
        }
        return finalizedList
    }
}