Loading packages/SystemUI/Android.bp +0 −1 Original line number Diff line number Diff line Loading @@ -60,7 +60,6 @@ systemui_compose_java_defaults { // except for SystemUI-core. // Copied from compose/features/Android.bp. static_libs: [ "CommunalLayoutLib", "PlatformComposeCore", "PlatformComposeSceneTransitionLayout", Loading packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +86 −61 Original line number Diff line number Diff line Loading @@ -4,8 +4,14 @@ import android.appwidget.AppWidgetHostView import android.os.Bundle import android.util.SizeF import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid import androidx.compose.material3.Card import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState Loading @@ -13,16 +19,12 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.integerResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.android.systemui.communal.layout.ui.compose.CommunalGridLayout import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.ui.model.CommunalContentUiModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.res.R @Composable fun CommunalHub( Loading @@ -34,68 +36,91 @@ fun CommunalHub( Box( modifier = modifier.fillMaxSize().background(Color.White), ) { CommunalGridLayout( modifier = Modifier.align(Alignment.CenterStart), layoutConfig = CommunalGridLayoutConfig( gridColumnSize = dimensionResource(R.dimen.communal_grid_column_size), gridGutter = dimensionResource(R.dimen.communal_grid_gutter_size), gridHeight = dimensionResource(R.dimen.communal_grid_height), gridColumnsPerCard = integerResource(R.integer.communal_grid_columns_per_card), ), communalCards = if (showTutorial) tutorialContent else widgetContent.map(::contentCard), LazyHorizontalGrid( modifier = modifier.height(Dimensions.GridHeight).align(Alignment.CenterStart), rows = GridCells.Fixed(CommunalContentSize.FULL.span), horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing), verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing), ) { if (showTutorial) { items( count = tutorialContentSizes.size, // TODO(b/308148193): a more scalable solution for unique ids. key = { index -> "tutorial_$index" }, span = { index -> GridItemSpan(tutorialContentSizes[index].span) }, ) { index -> TutorialCard( modifier = Modifier.size(Dimensions.CardWidth, tutorialContentSizes[index].dp()), ) } } private val tutorialContent = listOf( tutorialCard(CommunalGridLayoutCard.Size.FULL), tutorialCard(CommunalGridLayoutCard.Size.THIRD), tutorialCard(CommunalGridLayoutCard.Size.THIRD), tutorialCard(CommunalGridLayoutCard.Size.THIRD), tutorialCard(CommunalGridLayoutCard.Size.HALF), tutorialCard(CommunalGridLayoutCard.Size.HALF), tutorialCard(CommunalGridLayoutCard.Size.HALF), tutorialCard(CommunalGridLayoutCard.Size.HALF), } else { items( count = widgetContent.size, key = { index -> widgetContent[index].id }, span = { index -> GridItemSpan(widgetContent[index].size.span) }, ) { index -> val widget = widgetContent[index] ContentCard( modifier = Modifier.size(Dimensions.CardWidth, widget.size.dp()), model = widget, ) private fun tutorialCard(size: CommunalGridLayoutCard.Size): CommunalGridLayoutCard { return object : CommunalGridLayoutCard() { override val supportedSizes = listOf(size) @Composable override fun Content(modifier: Modifier, size: SizeF) { Card(modifier = modifier, content = {}) } } } } } private fun contentCard(model: CommunalContentUiModel): CommunalGridLayoutCard { return object : CommunalGridLayoutCard() { override val supportedSizes = listOf(convertToCardSize(model.size)) override val priority = model.priority // A placeholder for tutorial content. @Composable private fun TutorialCard(modifier: Modifier = Modifier) { Card(modifier = modifier, content = {}) } @Composable override fun Content(modifier: Modifier, size: SizeF) { private fun ContentCard( model: CommunalContentUiModel, modifier: Modifier = Modifier, ) { AndroidView( modifier = modifier, factory = { model.view.apply { if (this is AppWidgetHostView) { updateAppWidgetSize(Bundle(), listOf(size)) val size = SizeF(Dimensions.CardWidth.value, model.size.dp().value) updateAppWidgetSize(Bundle.EMPTY, listOf(size)) } } }, ) } private fun CommunalContentSize.dp(): Dp { return when (this) { CommunalContentSize.FULL -> Dimensions.CardHeightFull CommunalContentSize.HALF -> Dimensions.CardHeightHalf CommunalContentSize.THIRD -> Dimensions.CardHeightThird } } private fun convertToCardSize(size: CommunalContentSize): CommunalGridLayoutCard.Size { return when (size) { CommunalContentSize.FULL -> CommunalGridLayoutCard.Size.FULL CommunalContentSize.HALF -> CommunalGridLayoutCard.Size.HALF CommunalContentSize.THIRD -> CommunalGridLayoutCard.Size.THIRD } // Sizes for the tutorial placeholders. private val tutorialContentSizes = listOf( CommunalContentSize.FULL, CommunalContentSize.THIRD, CommunalContentSize.THIRD, CommunalContentSize.THIRD, CommunalContentSize.HALF, CommunalContentSize.HALF, CommunalContentSize.HALF, CommunalContentSize.HALF, ) private object Dimensions { val CardWidth = 464.dp val CardHeightFull = 630.dp val CardHeightHalf = 307.dp val CardHeightThird = 199.dp val GridHeight = CardHeightFull val Spacing = 16.dp } packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt +10 −5 Original line number Diff line number Diff line Loading @@ -16,14 +16,19 @@ package com.android.systemui.communal.shared.model /** Supported sizes for communal content in the layout grid. */ enum class CommunalContentSize { /** * Supported sizes for communal content in the layout grid. * * @param span The span of the content in a column. For example, if FULL is 6, then 3 represents * HALF, 2 represents THIRD, and 1 represents SIXTH. */ enum class CommunalContentSize(val span: Int) { /** Content takes the full height of the column. */ FULL, FULL(6), /** Content takes half of the height of the column. */ HALF, HALF(3), /** Content takes a third of the height of the column. */ THIRD, THIRD(2), } packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt +2 −2 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ import com.android.systemui.communal.shared.model.CommunalContentSize * This model stays in the UI layer. */ data class CommunalContentUiModel( val id: String, val view: View, val size: CommunalContentSize, val priority: Int, val size: CommunalContentSize = CommunalContentSize.HALF, ) packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +7 −8 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import android.appwidget.AppWidgetHost import android.content.Context import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.ui.model.CommunalContentUiModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application Loading @@ -42,16 +41,16 @@ constructor( /** List of widgets to be displayed in the communal hub. */ val widgetContent: Flow<List<CommunalContentUiModel>> = communalInteractor.widgetContent.map { it.map { communalInteractor.widgetContent.map { widgets -> widgets.map Widget@{ widget -> // TODO(b/306406256): As adding and removing widgets functionalities are // supported, cache the host views so they're not recreated each time. val hostView = appWidgetHost.createView(context, it.appWidgetId, it.providerInfo) return@map CommunalContentUiModel( val hostView = appWidgetHost.createView(context, widget.appWidgetId, widget.providerInfo) return@Widget CommunalContentUiModel( // TODO(b/308148193): a more scalable solution for unique ids. id = "widget_${widget.appWidgetId}", view = hostView, priority = it.priority, // All widgets have HALF size. size = CommunalContentSize.HALF, ) } } Loading Loading
packages/SystemUI/Android.bp +0 −1 Original line number Diff line number Diff line Loading @@ -60,7 +60,6 @@ systemui_compose_java_defaults { // except for SystemUI-core. // Copied from compose/features/Android.bp. static_libs: [ "CommunalLayoutLib", "PlatformComposeCore", "PlatformComposeSceneTransitionLayout", Loading
packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +86 −61 Original line number Diff line number Diff line Loading @@ -4,8 +4,14 @@ import android.appwidget.AppWidgetHostView import android.os.Bundle import android.util.SizeF import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid import androidx.compose.material3.Card import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState Loading @@ -13,16 +19,12 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.integerResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.android.systemui.communal.layout.ui.compose.CommunalGridLayout import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.ui.model.CommunalContentUiModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.res.R @Composable fun CommunalHub( Loading @@ -34,68 +36,91 @@ fun CommunalHub( Box( modifier = modifier.fillMaxSize().background(Color.White), ) { CommunalGridLayout( modifier = Modifier.align(Alignment.CenterStart), layoutConfig = CommunalGridLayoutConfig( gridColumnSize = dimensionResource(R.dimen.communal_grid_column_size), gridGutter = dimensionResource(R.dimen.communal_grid_gutter_size), gridHeight = dimensionResource(R.dimen.communal_grid_height), gridColumnsPerCard = integerResource(R.integer.communal_grid_columns_per_card), ), communalCards = if (showTutorial) tutorialContent else widgetContent.map(::contentCard), LazyHorizontalGrid( modifier = modifier.height(Dimensions.GridHeight).align(Alignment.CenterStart), rows = GridCells.Fixed(CommunalContentSize.FULL.span), horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing), verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing), ) { if (showTutorial) { items( count = tutorialContentSizes.size, // TODO(b/308148193): a more scalable solution for unique ids. key = { index -> "tutorial_$index" }, span = { index -> GridItemSpan(tutorialContentSizes[index].span) }, ) { index -> TutorialCard( modifier = Modifier.size(Dimensions.CardWidth, tutorialContentSizes[index].dp()), ) } } private val tutorialContent = listOf( tutorialCard(CommunalGridLayoutCard.Size.FULL), tutorialCard(CommunalGridLayoutCard.Size.THIRD), tutorialCard(CommunalGridLayoutCard.Size.THIRD), tutorialCard(CommunalGridLayoutCard.Size.THIRD), tutorialCard(CommunalGridLayoutCard.Size.HALF), tutorialCard(CommunalGridLayoutCard.Size.HALF), tutorialCard(CommunalGridLayoutCard.Size.HALF), tutorialCard(CommunalGridLayoutCard.Size.HALF), } else { items( count = widgetContent.size, key = { index -> widgetContent[index].id }, span = { index -> GridItemSpan(widgetContent[index].size.span) }, ) { index -> val widget = widgetContent[index] ContentCard( modifier = Modifier.size(Dimensions.CardWidth, widget.size.dp()), model = widget, ) private fun tutorialCard(size: CommunalGridLayoutCard.Size): CommunalGridLayoutCard { return object : CommunalGridLayoutCard() { override val supportedSizes = listOf(size) @Composable override fun Content(modifier: Modifier, size: SizeF) { Card(modifier = modifier, content = {}) } } } } } private fun contentCard(model: CommunalContentUiModel): CommunalGridLayoutCard { return object : CommunalGridLayoutCard() { override val supportedSizes = listOf(convertToCardSize(model.size)) override val priority = model.priority // A placeholder for tutorial content. @Composable private fun TutorialCard(modifier: Modifier = Modifier) { Card(modifier = modifier, content = {}) } @Composable override fun Content(modifier: Modifier, size: SizeF) { private fun ContentCard( model: CommunalContentUiModel, modifier: Modifier = Modifier, ) { AndroidView( modifier = modifier, factory = { model.view.apply { if (this is AppWidgetHostView) { updateAppWidgetSize(Bundle(), listOf(size)) val size = SizeF(Dimensions.CardWidth.value, model.size.dp().value) updateAppWidgetSize(Bundle.EMPTY, listOf(size)) } } }, ) } private fun CommunalContentSize.dp(): Dp { return when (this) { CommunalContentSize.FULL -> Dimensions.CardHeightFull CommunalContentSize.HALF -> Dimensions.CardHeightHalf CommunalContentSize.THIRD -> Dimensions.CardHeightThird } } private fun convertToCardSize(size: CommunalContentSize): CommunalGridLayoutCard.Size { return when (size) { CommunalContentSize.FULL -> CommunalGridLayoutCard.Size.FULL CommunalContentSize.HALF -> CommunalGridLayoutCard.Size.HALF CommunalContentSize.THIRD -> CommunalGridLayoutCard.Size.THIRD } // Sizes for the tutorial placeholders. private val tutorialContentSizes = listOf( CommunalContentSize.FULL, CommunalContentSize.THIRD, CommunalContentSize.THIRD, CommunalContentSize.THIRD, CommunalContentSize.HALF, CommunalContentSize.HALF, CommunalContentSize.HALF, CommunalContentSize.HALF, ) private object Dimensions { val CardWidth = 464.dp val CardHeightFull = 630.dp val CardHeightHalf = 307.dp val CardHeightThird = 199.dp val GridHeight = CardHeightFull val Spacing = 16.dp }
packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt +10 −5 Original line number Diff line number Diff line Loading @@ -16,14 +16,19 @@ package com.android.systemui.communal.shared.model /** Supported sizes for communal content in the layout grid. */ enum class CommunalContentSize { /** * Supported sizes for communal content in the layout grid. * * @param span The span of the content in a column. For example, if FULL is 6, then 3 represents * HALF, 2 represents THIRD, and 1 represents SIXTH. */ enum class CommunalContentSize(val span: Int) { /** Content takes the full height of the column. */ FULL, FULL(6), /** Content takes half of the height of the column. */ HALF, HALF(3), /** Content takes a third of the height of the column. */ THIRD, THIRD(2), }
packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt +2 −2 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ import com.android.systemui.communal.shared.model.CommunalContentSize * This model stays in the UI layer. */ data class CommunalContentUiModel( val id: String, val view: View, val size: CommunalContentSize, val priority: Int, val size: CommunalContentSize = CommunalContentSize.HALF, )
packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +7 −8 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import android.appwidget.AppWidgetHost import android.content.Context import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.ui.model.CommunalContentUiModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application Loading @@ -42,16 +41,16 @@ constructor( /** List of widgets to be displayed in the communal hub. */ val widgetContent: Flow<List<CommunalContentUiModel>> = communalInteractor.widgetContent.map { it.map { communalInteractor.widgetContent.map { widgets -> widgets.map Widget@{ widget -> // TODO(b/306406256): As adding and removing widgets functionalities are // supported, cache the host views so they're not recreated each time. val hostView = appWidgetHost.createView(context, it.appWidgetId, it.providerInfo) return@map CommunalContentUiModel( val hostView = appWidgetHost.createView(context, widget.appWidgetId, widget.providerInfo) return@Widget CommunalContentUiModel( // TODO(b/308148193): a more scalable solution for unique ids. id = "widget_${widget.appWidgetId}", view = hostView, priority = it.priority, // All widgets have HALF size. size = CommunalContentSize.HALF, ) } } Loading