Loading packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/layout/FakeComponentsLayoutManager.kt→packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/captioning/CaptioningModule.kt +21 −0 Original line number Diff line number Diff line Loading @@ -14,24 +14,8 @@ * limitations under the License. */ package com.android.systemui.volume.panel.ui.layout package com.android.systemui.volume.panel.component.captioning import com.android.systemui.volume.panel.ui.viewmodel.ComponentState import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState import dagger.Module class FakeComponentsLayoutManager( private val isBottomBar: (components: ComponentState) -> Boolean ) : ComponentsLayoutManager { override fun layout( volumePanelState: VolumePanelState, components: Collection<ComponentState> ): ComponentsLayout { return ComponentsLayout( components .filter { componentState -> !isBottomBar(componentState) } .sortedBy { it.key }, components.find(isBottomBar)!!, ) } } @Module interface CaptioningModule packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt 0 → 100644 +79 −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.volume.panel.component.button.ui.composable import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedIconToggleButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.android.systemui.common.ui.compose.Icon import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope import kotlinx.coroutines.flow.StateFlow class ToggleButtonComponent( private val viewModelFlow: StateFlow<ToggleButtonViewModel?>, private val onCheckedChange: (isChecked: Boolean) -> Unit ) : ComposeVolumePanelUiComponent { @Composable override fun VolumePanelComposeScope.Content(modifier: Modifier) { val viewModelByState by viewModelFlow.collectAsState() val viewModel = viewModelByState ?: return Column( modifier = modifier, verticalArrangement = Arrangement.spacedBy(12.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { OutlinedIconToggleButton( modifier = Modifier.height(64.dp).fillMaxWidth(), checked = viewModel.isChecked, onCheckedChange = onCheckedChange, colors = IconButtonDefaults.outlinedIconToggleButtonColors( containerColor = MaterialTheme.colorScheme.surface, contentColor = MaterialTheme.colorScheme.onSurfaceVariant, checkedContainerColor = MaterialTheme.colorScheme.primaryContainer, checkedContentColor = MaterialTheme.colorScheme.onPrimaryContainer, ), border = BorderStroke(8.dp, MaterialTheme.colorScheme.surface), ) { Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon) } Text( text = viewModel.label.toString(), style = MaterialTheme.typography.labelMedium, maxLines = 1, overflow = TextOverflow.Ellipsis, ) } } } packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/captioning/CaptioningModule.kt 0 → 100644 +53 −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.volume.panel.component.captioning import com.android.systemui.volume.panel.component.button.ui.composable.ToggleButtonComponent import com.android.systemui.volume.panel.component.captioning.domain.CaptioningAvailabilityCriteria import com.android.systemui.volume.panel.component.captioning.ui.viewmodel.CaptioningViewModel import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent import dagger.Binds import dagger.Module import dagger.Provides import dagger.multibindings.IntoMap import dagger.multibindings.StringKey /** Dagger module, that provides Captioning Volume Panel UI functionality. */ @Module interface CaptioningModule { @Binds @IntoMap @StringKey(VolumePanelComponents.CAPTIONING) fun bindComponentAvailabilityCriteria( criteria: CaptioningAvailabilityCriteria ): ComponentAvailabilityCriteria companion object { @Provides @IntoMap @StringKey(VolumePanelComponents.CAPTIONING) fun provideVolumePanelUiComponent(viewModel: CaptioningViewModel): VolumePanelUiComponent = ToggleButtonComponent( viewModel.buttonViewModel, viewModel::setIsSystemAudioCaptioningEnabled, ) } } packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt +18 −5 Original line number Diff line number Diff line Loading @@ -19,26 +19,39 @@ package com.android.systemui.volume.panel.ui.composable import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.material3.Slider import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.android.systemui.volume.panel.ui.viewmodel.ComponentState import com.android.systemui.volume.panel.ui.layout.ComponentsLayout @Composable fun VolumePanelComposeScope.VerticalVolumePanelContent( components: List<ComponentState>, layout: ComponentsLayout, modifier: Modifier = Modifier, ) { Column( modifier = modifier, verticalArrangement = Arrangement.spacedBy(20.dp), ) { Slider(0.5f, {}) for (component in components) { for (component in layout.contentComponents) { AnimatedVisibility(component.isVisible) { with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) } } } if (layout.footerComponents.isNotEmpty()) { Row( modifier = Modifier.fillMaxWidth().wrapContentHeight(), horizontalArrangement = Arrangement.spacedBy(20.dp) ) { for (component in layout.footerComponents) { with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier.weight(1f)) } } } } } } packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt +17 −11 Original line number Diff line number Diff line Loading @@ -21,14 +21,15 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.MutableTransitionState import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface Loading @@ -37,7 +38,9 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.dimensionResource import com.android.compose.theme.PlatformTheme import com.android.systemui.res.R Loading @@ -64,14 +67,17 @@ fun VolumePanelRoot( } } Column( modifier = modifier .fillMaxSize() .statusBarsPadding() .clickable(onClick = { viewModel.dismissPanel() }), verticalArrangement = Arrangement.Bottom, Box( modifier = modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter, ) { Spacer( modifier = Modifier.fillMaxSize() .alpha(0.32f) .background(MaterialTheme.colorScheme.scrim) .clickable(onClick = { viewModel.dismissPanel() }) ) AnimatedVisibility( visibleState = transitionState, enter = slideInVertically { it }, Loading @@ -80,7 +86,7 @@ fun VolumePanelRoot( val radius = dimensionResource(R.dimen.volume_panel_corner_radius) Surface( shape = RoundedCornerShape(topStart = radius, topEnd = radius), color = MaterialTheme.colorScheme.surfaceBright, color = MaterialTheme.colorScheme.surfaceContainer, ) { Column { components?.let { componentsState -> Loading @@ -97,7 +103,7 @@ fun VolumePanelRoot( private fun VolumePanelComposeScope.Components(state: ComponentsLayout) { if (orientation == Configuration.ORIENTATION_PORTRAIT) { VerticalVolumePanelContent( components = state.contentComponents, state, modifier = Modifier.padding(dimensionResource(R.dimen.volume_panel_content_padding)), ) } else { Loading Loading
packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/layout/FakeComponentsLayoutManager.kt→packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/captioning/CaptioningModule.kt +21 −0 Original line number Diff line number Diff line Loading @@ -14,24 +14,8 @@ * limitations under the License. */ package com.android.systemui.volume.panel.ui.layout package com.android.systemui.volume.panel.component.captioning import com.android.systemui.volume.panel.ui.viewmodel.ComponentState import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState import dagger.Module class FakeComponentsLayoutManager( private val isBottomBar: (components: ComponentState) -> Boolean ) : ComponentsLayoutManager { override fun layout( volumePanelState: VolumePanelState, components: Collection<ComponentState> ): ComponentsLayout { return ComponentsLayout( components .filter { componentState -> !isBottomBar(componentState) } .sortedBy { it.key }, components.find(isBottomBar)!!, ) } } @Module interface CaptioningModule
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt 0 → 100644 +79 −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.volume.panel.component.button.ui.composable import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedIconToggleButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.android.systemui.common.ui.compose.Icon import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope import kotlinx.coroutines.flow.StateFlow class ToggleButtonComponent( private val viewModelFlow: StateFlow<ToggleButtonViewModel?>, private val onCheckedChange: (isChecked: Boolean) -> Unit ) : ComposeVolumePanelUiComponent { @Composable override fun VolumePanelComposeScope.Content(modifier: Modifier) { val viewModelByState by viewModelFlow.collectAsState() val viewModel = viewModelByState ?: return Column( modifier = modifier, verticalArrangement = Arrangement.spacedBy(12.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { OutlinedIconToggleButton( modifier = Modifier.height(64.dp).fillMaxWidth(), checked = viewModel.isChecked, onCheckedChange = onCheckedChange, colors = IconButtonDefaults.outlinedIconToggleButtonColors( containerColor = MaterialTheme.colorScheme.surface, contentColor = MaterialTheme.colorScheme.onSurfaceVariant, checkedContainerColor = MaterialTheme.colorScheme.primaryContainer, checkedContentColor = MaterialTheme.colorScheme.onPrimaryContainer, ), border = BorderStroke(8.dp, MaterialTheme.colorScheme.surface), ) { Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon) } Text( text = viewModel.label.toString(), style = MaterialTheme.typography.labelMedium, maxLines = 1, overflow = TextOverflow.Ellipsis, ) } } }
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/captioning/CaptioningModule.kt 0 → 100644 +53 −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.volume.panel.component.captioning import com.android.systemui.volume.panel.component.button.ui.composable.ToggleButtonComponent import com.android.systemui.volume.panel.component.captioning.domain.CaptioningAvailabilityCriteria import com.android.systemui.volume.panel.component.captioning.ui.viewmodel.CaptioningViewModel import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent import dagger.Binds import dagger.Module import dagger.Provides import dagger.multibindings.IntoMap import dagger.multibindings.StringKey /** Dagger module, that provides Captioning Volume Panel UI functionality. */ @Module interface CaptioningModule { @Binds @IntoMap @StringKey(VolumePanelComponents.CAPTIONING) fun bindComponentAvailabilityCriteria( criteria: CaptioningAvailabilityCriteria ): ComponentAvailabilityCriteria companion object { @Provides @IntoMap @StringKey(VolumePanelComponents.CAPTIONING) fun provideVolumePanelUiComponent(viewModel: CaptioningViewModel): VolumePanelUiComponent = ToggleButtonComponent( viewModel.buttonViewModel, viewModel::setIsSystemAudioCaptioningEnabled, ) } }
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt +18 −5 Original line number Diff line number Diff line Loading @@ -19,26 +19,39 @@ package com.android.systemui.volume.panel.ui.composable import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.material3.Slider import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.android.systemui.volume.panel.ui.viewmodel.ComponentState import com.android.systemui.volume.panel.ui.layout.ComponentsLayout @Composable fun VolumePanelComposeScope.VerticalVolumePanelContent( components: List<ComponentState>, layout: ComponentsLayout, modifier: Modifier = Modifier, ) { Column( modifier = modifier, verticalArrangement = Arrangement.spacedBy(20.dp), ) { Slider(0.5f, {}) for (component in components) { for (component in layout.contentComponents) { AnimatedVisibility(component.isVisible) { with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) } } } if (layout.footerComponents.isNotEmpty()) { Row( modifier = Modifier.fillMaxWidth().wrapContentHeight(), horizontalArrangement = Arrangement.spacedBy(20.dp) ) { for (component in layout.footerComponents) { with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier.weight(1f)) } } } } } }
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt +17 −11 Original line number Diff line number Diff line Loading @@ -21,14 +21,15 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.MutableTransitionState import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface Loading @@ -37,7 +38,9 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.dimensionResource import com.android.compose.theme.PlatformTheme import com.android.systemui.res.R Loading @@ -64,14 +67,17 @@ fun VolumePanelRoot( } } Column( modifier = modifier .fillMaxSize() .statusBarsPadding() .clickable(onClick = { viewModel.dismissPanel() }), verticalArrangement = Arrangement.Bottom, Box( modifier = modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter, ) { Spacer( modifier = Modifier.fillMaxSize() .alpha(0.32f) .background(MaterialTheme.colorScheme.scrim) .clickable(onClick = { viewModel.dismissPanel() }) ) AnimatedVisibility( visibleState = transitionState, enter = slideInVertically { it }, Loading @@ -80,7 +86,7 @@ fun VolumePanelRoot( val radius = dimensionResource(R.dimen.volume_panel_corner_radius) Surface( shape = RoundedCornerShape(topStart = radius, topEnd = radius), color = MaterialTheme.colorScheme.surfaceBright, color = MaterialTheme.colorScheme.surfaceContainer, ) { Column { components?.let { componentsState -> Loading @@ -97,7 +103,7 @@ fun VolumePanelRoot( private fun VolumePanelComposeScope.Components(state: ComponentsLayout) { if (orientation == Configuration.ORIENTATION_PORTRAIT) { VerticalVolumePanelContent( components = state.contentComponents, state, modifier = Modifier.padding(dimensionResource(R.dimen.volume_panel_content_padding)), ) } else { Loading