Loading packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt +2 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import androidx.lifecycle.LifecycleOwner import com.android.systemui.bouncer.ui.BouncerDialogFactory import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.scene.shared.model.Scene Loading Loading @@ -52,6 +53,7 @@ object ComposeFacade : BaseComposeFacade { override fun setCommunalEditWidgetActivityContent( activity: ComponentActivity, viewModel: BaseCommunalViewModel, widgetConfigurator: WidgetConfigurator, onOpenWidgetPicker: () -> Unit, onEditDone: () -> Unit, ) { Loading packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt +3 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.android.systemui.common.ui.compose.windowinsets.DisplayCutoutProvider import com.android.systemui.communal.ui.compose.CommunalContainer import com.android.systemui.communal.ui.compose.CommunalHub import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.people.ui.compose.PeopleScreen import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.compose.FooterActions Loading Loading @@ -69,6 +70,7 @@ object ComposeFacade : BaseComposeFacade { override fun setCommunalEditWidgetActivityContent( activity: ComponentActivity, viewModel: BaseCommunalViewModel, widgetConfigurator: WidgetConfigurator, onOpenWidgetPicker: () -> Unit, onEditDone: () -> Unit, ) { Loading @@ -77,6 +79,7 @@ object ComposeFacade : BaseComposeFacade { CommunalHub( viewModel = viewModel, onOpenWidgetPicker = onOpenWidgetPicker, widgetConfigurator = widgetConfigurator, onEditDone = onEditDone, ) } Loading packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +76 −7 Original line number Diff line number Diff line Loading @@ -20,7 +20,10 @@ import android.appwidget.AppWidgetHostView import android.os.Bundle import android.util.SizeF import android.widget.FrameLayout import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background Loading @@ -47,6 +50,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.TouchApp import androidx.compose.material.icons.outlined.Widgets import androidx.compose.material3.Button Loading @@ -54,8 +58,10 @@ import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.FilledIconButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonColors import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text Loading @@ -66,6 +72,7 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier Loading Loading @@ -99,12 +106,15 @@ import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset import com.android.systemui.communal.ui.compose.extensions.observeTapsWithoutConsuming import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.res.R import kotlinx.coroutines.launch @Composable fun CommunalHub( modifier: Modifier = Modifier, viewModel: BaseCommunalViewModel, widgetConfigurator: WidgetConfigurator? = null, onOpenWidgetPicker: (() -> Unit)? = null, onEditDone: (() -> Unit)? = null, ) { Loading @@ -116,7 +126,7 @@ fun CommunalHub( var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } var isDraggingToRemove by remember { mutableStateOf(false) } val gridState = rememberLazyGridState() val contentListState = rememberContentListState(communalContent, viewModel) val contentListState = rememberContentListState(widgetConfigurator, communalContent, viewModel) val reorderingWidgets by viewModel.reorderingWidgets.collectAsState() val selectedIndex = viewModel.selectedIndex.collectAsState() val removeButtonEnabled by remember { Loading Loading @@ -167,7 +177,8 @@ fun CommunalHub( onOpenWidgetPicker = onOpenWidgetPicker, gridState = gridState, contentListState = contentListState, selectedIndex = selectedIndex selectedIndex = selectedIndex, widgetConfigurator = widgetConfigurator, ) if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) { Loading Loading @@ -221,6 +232,7 @@ private fun BoxScope.CommunalHubLazyGrid( setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit, updateDragPositionForRemove: (offset: Offset) -> Boolean, onOpenWidgetPicker: (() -> Unit)? = null, widgetConfigurator: WidgetConfigurator?, ) { var gridModifier = Modifier.align(Alignment.CenterStart) var list = communalContent Loading Loading @@ -283,21 +295,24 @@ private fun BoxScope.CommunalHubLazyGrid( enabled = list[index] is CommunalContentModel.Widget, index = index, size = size ) { _ -> ) { isDragging -> CommunalContent( modifier = cardModifier, model = list[index], viewModel = viewModel, size = size, onOpenWidgetPicker = onOpenWidgetPicker, selected = selected && !isDragging, widgetConfigurator = widgetConfigurator, ) } } else { CommunalContent( modifier = cardModifier, model = list[index], viewModel = viewModel, size = size, selected = false, modifier = cardModifier, ) } } Loading Loading @@ -453,11 +468,14 @@ private fun CommunalContent( model: CommunalContentModel, viewModel: BaseCommunalViewModel, size: SizeF, selected: Boolean, modifier: Modifier = Modifier, onOpenWidgetPicker: (() -> Unit)? = null, widgetConfigurator: WidgetConfigurator? = null, ) { when (model) { is CommunalContentModel.Widget -> WidgetContent(viewModel, model, size, modifier) is CommunalContentModel.Widget -> WidgetContent(viewModel, model, size, selected, widgetConfigurator, modifier) is CommunalContentModel.WidgetPlaceholder -> HighlightedItem(size) is CommunalContentModel.CtaTileInViewMode -> CtaTileInViewModeContent(viewModel, size, modifier) Loading Loading @@ -594,15 +612,17 @@ private fun WidgetContent( viewModel: BaseCommunalViewModel, model: CommunalContentModel.Widget, size: SizeF, selected: Boolean, widgetConfigurator: WidgetConfigurator?, modifier: Modifier = Modifier, ) { Box( modifier = modifier.height(size.height.dp), contentAlignment = Alignment.Center, ) { val paddingInPx = with(LocalDensity.current) { CardOutlineWidth.toPx().toInt() } AndroidView( modifier = modifier.allowGestures(allowed = !viewModel.isEditMode), modifier = modifier.align(Alignment.Center).allowGestures(allowed = !viewModel.isEditMode), factory = { context -> // The AppWidgetHostView will inherit the interaction handler from the // AppWidgetHost. So set the interaction handler here before creating the view, and Loading @@ -624,6 +644,55 @@ private fun WidgetContent( // For reusing composition in lazy lists. onReset = {}, ) if ( viewModel is CommunalEditModeViewModel && model.reconfigurable && widgetConfigurator != null ) { WidgetConfigureButton( visible = selected, model = model, widgetConfigurator = widgetConfigurator, modifier = Modifier.align(Alignment.BottomEnd) ) } } } @Composable fun WidgetConfigureButton( visible: Boolean, model: CommunalContentModel.Widget, modifier: Modifier = Modifier, widgetConfigurator: WidgetConfigurator, ) { val colors = LocalAndroidColorScheme.current val scope = rememberCoroutineScope() AnimatedVisibility( visible = visible, enter = fadeIn(), exit = fadeOut(), modifier = modifier.padding(16.dp), ) { FilledIconButton( shape = RoundedCornerShape(16.dp), modifier = Modifier.size(48.dp), colors = IconButtonColors( containerColor = colors.primary, contentColor = colors.onPrimary, disabledContainerColor = Color.Transparent, disabledContentColor = Color.Transparent ), onClick = { scope.launch { widgetConfigurator.configureWidget(model.appWidgetId) } }, ) { Icon( imageVector = Icons.Outlined.Edit, contentDescription = stringResource(id = R.string.edit_widget), modifier = Modifier.padding(12.dp) ) } } } Loading packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt +9 −1 Original line number Diff line number Diff line Loading @@ -22,16 +22,24 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.toMutableStateList import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.widgets.WidgetConfigurator @Composable fun rememberContentListState( widgetConfigurator: WidgetConfigurator?, communalContent: List<CommunalContentModel>, viewModel: BaseCommunalViewModel, ): ContentListState { return remember(communalContent) { ContentListState( communalContent, viewModel::onAddWidget, { componentName, priority -> viewModel.onAddWidget( componentName, priority, widgetConfigurator, ) }, viewModel::onDeleteWidget, viewModel::onReorderWidgets, ) Loading packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt +39 −16 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.systemui.communal.data.repository import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetProviderInfo import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE import android.content.ComponentName import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest Loading @@ -28,6 +30,9 @@ import com.android.systemui.communal.data.db.CommunalWidgetItem import com.android.systemui.communal.shared.CommunalWidgetHost import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.communal.widgets.widgetConfiguratorFail import com.android.systemui.communal.widgets.widgetConfiguratorSuccess import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope Loading Loading @@ -71,12 +76,12 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var communalWidgetDao: CommunalWidgetDao private val kosmos = testKosmos() private lateinit var logBuffer: LogBuffer private val kosmos = testKosmos() private val testDispatcher = kosmos.testDispatcher private val testScope = kosmos.testScope private lateinit var logBuffer: LogBuffer private val fakeAllowlist = listOf( "com.android.fake/WidgetProviderA", Loading Loading @@ -157,10 +162,11 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val priority = 1 whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(true) whenever(communalWidgetHost.getAppWidgetInfo(id)) .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION) whenever(communalWidgetHost.allocateIdAndBindWidget(any<ComponentName>())) .thenReturn(id) underTest.addWidget(provider, priority) { true } underTest.addWidget(provider, priority, kosmos.widgetConfiguratorSuccess) runCurrent() verify(communalWidgetHost).allocateIdAndBindWidget(provider) Loading @@ -175,9 +181,10 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val priority = 1 whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(true) whenever(communalWidgetHost.getAppWidgetInfo(id)) .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION) whenever(communalWidgetHost.allocateIdAndBindWidget(provider)).thenReturn(id) underTest.addWidget(provider, priority) { false } underTest.addWidget(provider, priority, kosmos.widgetConfiguratorFail) runCurrent() verify(communalWidgetHost).allocateIdAndBindWidget(provider) Loading @@ -193,9 +200,18 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val priority = 1 whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(true) whenever(communalWidgetHost.getAppWidgetInfo(id)) .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION) whenever(communalWidgetHost.allocateIdAndBindWidget(provider)).thenReturn(id) underTest.addWidget(provider, priority) { throw IllegalStateException("some error") } underTest.addWidget( provider, priority, object : WidgetConfigurator { override suspend fun configureWidget(appWidgetId: Int): Boolean { throw IllegalStateException("some error") } } ) runCurrent() verify(communalWidgetHost).allocateIdAndBindWidget(provider) Loading @@ -211,19 +227,15 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val priority = 1 whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(false) whenever(communalWidgetHost.getAppWidgetInfo(id)) .thenReturn(PROVIDER_INFO_CONFIGURATION_OPTIONAL) whenever(communalWidgetHost.allocateIdAndBindWidget(any<ComponentName>())) .thenReturn(id) var configured = false underTest.addWidget(provider, priority) { configured = true true } underTest.addWidget(provider, priority, kosmos.widgetConfiguratorFail) runCurrent() verify(communalWidgetHost).allocateIdAndBindWidget(provider) verify(communalWidgetDao).addWidget(id, provider, priority) assertThat(configured).isFalse() } @Test Loading Loading @@ -280,4 +292,15 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { private fun setAppWidgetIds(ids: List<Int>) { whenever(appWidgetHost.appWidgetIds).thenReturn(ids.toIntArray()) } private companion object { val PROVIDER_INFO_REQUIRES_CONFIGURATION = AppWidgetProviderInfo().apply { configure = ComponentName("test.pkg", "test.cmp") } val PROVIDER_INFO_CONFIGURATION_OPTIONAL = AppWidgetProviderInfo().apply { configure = ComponentName("test.pkg", "test.cmp") widgetFeatures = WIDGET_FEATURE_CONFIGURATION_OPTIONAL or WIDGET_FEATURE_RECONFIGURABLE } } } Loading
packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt +2 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import androidx.lifecycle.LifecycleOwner import com.android.systemui.bouncer.ui.BouncerDialogFactory import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.scene.shared.model.Scene Loading Loading @@ -52,6 +53,7 @@ object ComposeFacade : BaseComposeFacade { override fun setCommunalEditWidgetActivityContent( activity: ComponentActivity, viewModel: BaseCommunalViewModel, widgetConfigurator: WidgetConfigurator, onOpenWidgetPicker: () -> Unit, onEditDone: () -> Unit, ) { Loading
packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt +3 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.android.systemui.common.ui.compose.windowinsets.DisplayCutoutProvider import com.android.systemui.communal.ui.compose.CommunalContainer import com.android.systemui.communal.ui.compose.CommunalHub import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.people.ui.compose.PeopleScreen import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.compose.FooterActions Loading Loading @@ -69,6 +70,7 @@ object ComposeFacade : BaseComposeFacade { override fun setCommunalEditWidgetActivityContent( activity: ComponentActivity, viewModel: BaseCommunalViewModel, widgetConfigurator: WidgetConfigurator, onOpenWidgetPicker: () -> Unit, onEditDone: () -> Unit, ) { Loading @@ -77,6 +79,7 @@ object ComposeFacade : BaseComposeFacade { CommunalHub( viewModel = viewModel, onOpenWidgetPicker = onOpenWidgetPicker, widgetConfigurator = widgetConfigurator, onEditDone = onEditDone, ) } Loading
packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +76 −7 Original line number Diff line number Diff line Loading @@ -20,7 +20,10 @@ import android.appwidget.AppWidgetHostView import android.os.Bundle import android.util.SizeF import android.widget.FrameLayout import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background Loading @@ -47,6 +50,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.TouchApp import androidx.compose.material.icons.outlined.Widgets import androidx.compose.material3.Button Loading @@ -54,8 +58,10 @@ import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.FilledIconButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonColors import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text Loading @@ -66,6 +72,7 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier Loading Loading @@ -99,12 +106,15 @@ import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset import com.android.systemui.communal.ui.compose.extensions.observeTapsWithoutConsuming import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.res.R import kotlinx.coroutines.launch @Composable fun CommunalHub( modifier: Modifier = Modifier, viewModel: BaseCommunalViewModel, widgetConfigurator: WidgetConfigurator? = null, onOpenWidgetPicker: (() -> Unit)? = null, onEditDone: (() -> Unit)? = null, ) { Loading @@ -116,7 +126,7 @@ fun CommunalHub( var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } var isDraggingToRemove by remember { mutableStateOf(false) } val gridState = rememberLazyGridState() val contentListState = rememberContentListState(communalContent, viewModel) val contentListState = rememberContentListState(widgetConfigurator, communalContent, viewModel) val reorderingWidgets by viewModel.reorderingWidgets.collectAsState() val selectedIndex = viewModel.selectedIndex.collectAsState() val removeButtonEnabled by remember { Loading Loading @@ -167,7 +177,8 @@ fun CommunalHub( onOpenWidgetPicker = onOpenWidgetPicker, gridState = gridState, contentListState = contentListState, selectedIndex = selectedIndex selectedIndex = selectedIndex, widgetConfigurator = widgetConfigurator, ) if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) { Loading Loading @@ -221,6 +232,7 @@ private fun BoxScope.CommunalHubLazyGrid( setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit, updateDragPositionForRemove: (offset: Offset) -> Boolean, onOpenWidgetPicker: (() -> Unit)? = null, widgetConfigurator: WidgetConfigurator?, ) { var gridModifier = Modifier.align(Alignment.CenterStart) var list = communalContent Loading Loading @@ -283,21 +295,24 @@ private fun BoxScope.CommunalHubLazyGrid( enabled = list[index] is CommunalContentModel.Widget, index = index, size = size ) { _ -> ) { isDragging -> CommunalContent( modifier = cardModifier, model = list[index], viewModel = viewModel, size = size, onOpenWidgetPicker = onOpenWidgetPicker, selected = selected && !isDragging, widgetConfigurator = widgetConfigurator, ) } } else { CommunalContent( modifier = cardModifier, model = list[index], viewModel = viewModel, size = size, selected = false, modifier = cardModifier, ) } } Loading Loading @@ -453,11 +468,14 @@ private fun CommunalContent( model: CommunalContentModel, viewModel: BaseCommunalViewModel, size: SizeF, selected: Boolean, modifier: Modifier = Modifier, onOpenWidgetPicker: (() -> Unit)? = null, widgetConfigurator: WidgetConfigurator? = null, ) { when (model) { is CommunalContentModel.Widget -> WidgetContent(viewModel, model, size, modifier) is CommunalContentModel.Widget -> WidgetContent(viewModel, model, size, selected, widgetConfigurator, modifier) is CommunalContentModel.WidgetPlaceholder -> HighlightedItem(size) is CommunalContentModel.CtaTileInViewMode -> CtaTileInViewModeContent(viewModel, size, modifier) Loading Loading @@ -594,15 +612,17 @@ private fun WidgetContent( viewModel: BaseCommunalViewModel, model: CommunalContentModel.Widget, size: SizeF, selected: Boolean, widgetConfigurator: WidgetConfigurator?, modifier: Modifier = Modifier, ) { Box( modifier = modifier.height(size.height.dp), contentAlignment = Alignment.Center, ) { val paddingInPx = with(LocalDensity.current) { CardOutlineWidth.toPx().toInt() } AndroidView( modifier = modifier.allowGestures(allowed = !viewModel.isEditMode), modifier = modifier.align(Alignment.Center).allowGestures(allowed = !viewModel.isEditMode), factory = { context -> // The AppWidgetHostView will inherit the interaction handler from the // AppWidgetHost. So set the interaction handler here before creating the view, and Loading @@ -624,6 +644,55 @@ private fun WidgetContent( // For reusing composition in lazy lists. onReset = {}, ) if ( viewModel is CommunalEditModeViewModel && model.reconfigurable && widgetConfigurator != null ) { WidgetConfigureButton( visible = selected, model = model, widgetConfigurator = widgetConfigurator, modifier = Modifier.align(Alignment.BottomEnd) ) } } } @Composable fun WidgetConfigureButton( visible: Boolean, model: CommunalContentModel.Widget, modifier: Modifier = Modifier, widgetConfigurator: WidgetConfigurator, ) { val colors = LocalAndroidColorScheme.current val scope = rememberCoroutineScope() AnimatedVisibility( visible = visible, enter = fadeIn(), exit = fadeOut(), modifier = modifier.padding(16.dp), ) { FilledIconButton( shape = RoundedCornerShape(16.dp), modifier = Modifier.size(48.dp), colors = IconButtonColors( containerColor = colors.primary, contentColor = colors.onPrimary, disabledContainerColor = Color.Transparent, disabledContentColor = Color.Transparent ), onClick = { scope.launch { widgetConfigurator.configureWidget(model.appWidgetId) } }, ) { Icon( imageVector = Icons.Outlined.Edit, contentDescription = stringResource(id = R.string.edit_widget), modifier = Modifier.padding(12.dp) ) } } } Loading
packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt +9 −1 Original line number Diff line number Diff line Loading @@ -22,16 +22,24 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.toMutableStateList import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.widgets.WidgetConfigurator @Composable fun rememberContentListState( widgetConfigurator: WidgetConfigurator?, communalContent: List<CommunalContentModel>, viewModel: BaseCommunalViewModel, ): ContentListState { return remember(communalContent) { ContentListState( communalContent, viewModel::onAddWidget, { componentName, priority -> viewModel.onAddWidget( componentName, priority, widgetConfigurator, ) }, viewModel::onDeleteWidget, viewModel::onReorderWidgets, ) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt +39 −16 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.systemui.communal.data.repository import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetProviderInfo import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE import android.content.ComponentName import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest Loading @@ -28,6 +30,9 @@ import com.android.systemui.communal.data.db.CommunalWidgetItem import com.android.systemui.communal.shared.CommunalWidgetHost import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.communal.widgets.widgetConfiguratorFail import com.android.systemui.communal.widgets.widgetConfiguratorSuccess import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope Loading Loading @@ -71,12 +76,12 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var communalWidgetDao: CommunalWidgetDao private val kosmos = testKosmos() private lateinit var logBuffer: LogBuffer private val kosmos = testKosmos() private val testDispatcher = kosmos.testDispatcher private val testScope = kosmos.testScope private lateinit var logBuffer: LogBuffer private val fakeAllowlist = listOf( "com.android.fake/WidgetProviderA", Loading Loading @@ -157,10 +162,11 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val priority = 1 whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(true) whenever(communalWidgetHost.getAppWidgetInfo(id)) .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION) whenever(communalWidgetHost.allocateIdAndBindWidget(any<ComponentName>())) .thenReturn(id) underTest.addWidget(provider, priority) { true } underTest.addWidget(provider, priority, kosmos.widgetConfiguratorSuccess) runCurrent() verify(communalWidgetHost).allocateIdAndBindWidget(provider) Loading @@ -175,9 +181,10 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val priority = 1 whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(true) whenever(communalWidgetHost.getAppWidgetInfo(id)) .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION) whenever(communalWidgetHost.allocateIdAndBindWidget(provider)).thenReturn(id) underTest.addWidget(provider, priority) { false } underTest.addWidget(provider, priority, kosmos.widgetConfiguratorFail) runCurrent() verify(communalWidgetHost).allocateIdAndBindWidget(provider) Loading @@ -193,9 +200,18 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val priority = 1 whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(true) whenever(communalWidgetHost.getAppWidgetInfo(id)) .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION) whenever(communalWidgetHost.allocateIdAndBindWidget(provider)).thenReturn(id) underTest.addWidget(provider, priority) { throw IllegalStateException("some error") } underTest.addWidget( provider, priority, object : WidgetConfigurator { override suspend fun configureWidget(appWidgetId: Int): Boolean { throw IllegalStateException("some error") } } ) runCurrent() verify(communalWidgetHost).allocateIdAndBindWidget(provider) Loading @@ -211,19 +227,15 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val priority = 1 whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(false) whenever(communalWidgetHost.getAppWidgetInfo(id)) .thenReturn(PROVIDER_INFO_CONFIGURATION_OPTIONAL) whenever(communalWidgetHost.allocateIdAndBindWidget(any<ComponentName>())) .thenReturn(id) var configured = false underTest.addWidget(provider, priority) { configured = true true } underTest.addWidget(provider, priority, kosmos.widgetConfiguratorFail) runCurrent() verify(communalWidgetHost).allocateIdAndBindWidget(provider) verify(communalWidgetDao).addWidget(id, provider, priority) assertThat(configured).isFalse() } @Test Loading Loading @@ -280,4 +292,15 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { private fun setAppWidgetIds(ids: List<Int>) { whenever(appWidgetHost.appWidgetIds).thenReturn(ids.toIntArray()) } private companion object { val PROVIDER_INFO_REQUIRES_CONFIGURATION = AppWidgetProviderInfo().apply { configure = ComponentName("test.pkg", "test.cmp") } val PROVIDER_INFO_CONFIGURATION_OPTIONAL = AppWidgetProviderInfo().apply { configure = ComponentName("test.pkg", "test.cmp") widgetFeatures = WIDGET_FEATURE_CONFIGURATION_OPTIONAL or WIDGET_FEATURE_RECONFIGURABLE } } }