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

Commit 3291b498 authored by Anton Potapov's avatar Anton Potapov
Browse files

Add captioning button to the Volume Panel

Plus a few changes to the infra:
 - fix volume panel styling;
 - rework ComponentsLayout to accomodate a separated buttons collection;
 - move FakeCaptioningRepository from SettingsLib to SystemUI to use it
   in the tests;

Flag: aconfig new_volume_panel DISABLED
Test: atest CaptioningViewModelTest
Test: atest DefaultComponentsLayoutManagerTest
Test: atest VolumePanelViewModelTest
Fixes: 324241246
Change-Id: I5277e92fe01b9e0ee65755e19f3c65ff7a710ed2
parent b2121237
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -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
+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,
            )
        }
    }
}
+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,
            )
    }
}
+18 −5
Original line number Diff line number Diff line
@@ -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))
                    }
                }
            }
        }
    }
}
+17 −11
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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 },
@@ -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 ->
@@ -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