Loading packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +11 −8 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.customActions import androidx.compose.ui.semantics.disabled import androidx.compose.ui.semantics.progressBarRangeInfo import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.setProgress import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.dp Loading Loading @@ -106,7 +107,10 @@ fun VolumeSlider( return } Column(modifier = modifier.animateContentSize(), verticalArrangement = Arrangement.Top) { Column( modifier = modifier.animateContentSize().semantics(true) {}, verticalArrangement = Arrangement.Top, ) { Row( horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth().height(40.dp), Loading @@ -123,7 +127,7 @@ fun VolumeSlider( text = state.label, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurface, modifier = Modifier.weight(1f), modifier = Modifier.weight(1f).clearAndSetSemantics {}, ) button?.invoke() } Loading @@ -134,12 +138,11 @@ fun VolumeSlider( onValueChanged = onValueChange, onValueChangeFinished = { onValueChangeFinished?.invoke() }, isEnabled = state.isEnabled, stepDistance = state.a11yStep, stepDistance = state.step, accessibilityParams = AccessibilityParams( label = state.label, disabledMessage = state.disabledMessage, currentStateDescription = state.a11yStateDescription, contentDescription = state.a11yContentDescription, stateDescription = state.a11yStateDescription, ), haptics = hapticsViewModelFactory?.let { Loading Loading @@ -169,7 +172,7 @@ fun VolumeSlider( text = disabledMessage, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.labelSmall, modifier = Modifier.basicMarquee(), modifier = Modifier.basicMarquee().clearAndSetSemantics {}, ) } } Loading Loading @@ -229,7 +232,7 @@ private fun LegacyVolumeSlider( } val newValue = (value + targetDirection * state.a11yStep).coerceIn( (value + targetDirection * state.step).coerceIn( state.valueRange.start, state.valueRange.endInclusive, ) Loading packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt +8 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel import android.bluetooth.BluetoothDevice import android.graphics.drawable.TestStubDrawable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.CachedBluetoothDevice Loading Loading @@ -63,6 +64,12 @@ class AudioSharingStreamSliderViewModelTest : SysuiTestCase() { assertThat(audioSharingSlider!!.label).isEqualTo("my headset 2") assertThat(audioSharingSlider!!.icon) .isEqualTo(Icon.Resource(R.drawable.ic_volume_media_bt, null)) .isEqualTo( Icon.Loaded( drawable = TestStubDrawable(), res = R.drawable.ic_volume_media_bt, contentDescription = null, ) ) } } packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt +8 −4 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel import android.app.Flags import android.app.NotificationManager.INTERRUPTION_FILTER_NONE import android.graphics.drawable.TestStubDrawable import android.media.AudioManager import android.platform.test.annotations.EnableFlags import android.service.notification.ZenPolicy Loading @@ -28,7 +29,6 @@ import com.android.settingslib.notification.modes.TestModeBuilder import com.android.settingslib.volume.shared.model.AudioStream import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.collectLastValue Loading @@ -39,8 +39,6 @@ import com.android.systemui.statusbar.policy.data.repository.fakeZenModeReposito import com.android.systemui.testKosmos import com.android.systemui.volume.data.repository.audioSharingRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock Loading Loading @@ -173,6 +171,12 @@ class AudioStreamSliderViewModelTest : SysuiTestCase() { assertThat(mediaSlider!!.label).isEqualTo("my headset 1") assertThat(mediaSlider!!.icon) .isEqualTo(Icon.Resource(R.drawable.ic_volume_media_bt, null)) .isEqualTo( Icon.Loaded( drawable = TestStubDrawable(), res = R.drawable.ic_volume_media_bt, contentDescription = null, ) ) } } packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt +32 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.common.shared.model import android.annotation.DrawableRes import android.graphics.drawable.Drawable import androidx.compose.runtime.Stable import com.android.systemui.common.shared.model.Icon.Loaded /** * Models an icon, that can either be already [loaded][Icon.Loaded] or be a [reference] Loading @@ -33,8 +34,37 @@ sealed class Icon { constructor( val drawable: Drawable, override val contentDescription: ContentDescription?, /** * Serves as an id to compare two instances. When provided this is used alongside * [contentDescription] to determine equality. This is useful when comparing icons * representing the same UI, but with different [drawable] instances. */ @DrawableRes val res: Int? = null, ) : Icon() ) : Icon() { override fun equals(other: Any?): Boolean { val that = other as? Loaded ?: return false if (this.res != null && that.res != null) { return this.res == that.res && this.contentDescription == that.contentDescription } return this.res == that.res && this.drawable == that.drawable && this.contentDescription == that.contentDescription } override fun hashCode(): Int { var result = contentDescription?.hashCode() ?: 0 result = if (res != null) { 31 * result + res.hashCode() } else { 31 * result + drawable.hashCode() } return result } } data class Resource( @DrawableRes val res: Int, Loading @@ -49,4 +79,4 @@ sealed class Icon { fun Drawable.asIcon( contentDescription: ContentDescription? = null, @DrawableRes res: Int? = null, ): Icon.Loaded = Icon.Loaded(this, contentDescription, res) ): Loaded = Loaded(this, contentDescription, res) packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt +5 −6 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.systemui.volume.dialog.sliders.ui import android.graphics.drawable.Drawable import android.view.View import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.tween Loading @@ -29,7 +28,6 @@ import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SliderDefaults import androidx.compose.runtime.Composable Loading @@ -43,7 +41,8 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.theme.PlatformTheme import com.android.compose.ui.graphics.painter.DrawablePainter import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.res.R Loading Loading @@ -155,7 +154,7 @@ private fun VolumeDialogSlider( }, ) }, accessibilityParams = AccessibilityParams(label = sliderStateModel.label), accessibilityParams = AccessibilityParams(contentDescription = sliderStateModel.label), modifier = modifier.pointerInput(Unit) { coroutineScope { Loading @@ -172,7 +171,7 @@ private fun VolumeDialogSlider( @Composable private fun BoxScope.VolumeIcon( drawable: Drawable, icon: Icon.Loaded, isVisible: Boolean, modifier: Modifier = Modifier, ) { Loading @@ -182,6 +181,6 @@ private fun BoxScope.VolumeIcon( exit = fadeOut(animationSpec = tween(durationMillis = 50)), modifier = modifier.align(Alignment.Center).size(40.dp).padding(10.dp), ) { Icon(painter = DrawablePainter(drawable), contentDescription = null) Icon(icon) } } Loading
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +11 −8 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.customActions import androidx.compose.ui.semantics.disabled import androidx.compose.ui.semantics.progressBarRangeInfo import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.setProgress import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.dp Loading Loading @@ -106,7 +107,10 @@ fun VolumeSlider( return } Column(modifier = modifier.animateContentSize(), verticalArrangement = Arrangement.Top) { Column( modifier = modifier.animateContentSize().semantics(true) {}, verticalArrangement = Arrangement.Top, ) { Row( horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth().height(40.dp), Loading @@ -123,7 +127,7 @@ fun VolumeSlider( text = state.label, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurface, modifier = Modifier.weight(1f), modifier = Modifier.weight(1f).clearAndSetSemantics {}, ) button?.invoke() } Loading @@ -134,12 +138,11 @@ fun VolumeSlider( onValueChanged = onValueChange, onValueChangeFinished = { onValueChangeFinished?.invoke() }, isEnabled = state.isEnabled, stepDistance = state.a11yStep, stepDistance = state.step, accessibilityParams = AccessibilityParams( label = state.label, disabledMessage = state.disabledMessage, currentStateDescription = state.a11yStateDescription, contentDescription = state.a11yContentDescription, stateDescription = state.a11yStateDescription, ), haptics = hapticsViewModelFactory?.let { Loading Loading @@ -169,7 +172,7 @@ fun VolumeSlider( text = disabledMessage, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.labelSmall, modifier = Modifier.basicMarquee(), modifier = Modifier.basicMarquee().clearAndSetSemantics {}, ) } } Loading Loading @@ -229,7 +232,7 @@ private fun LegacyVolumeSlider( } val newValue = (value + targetDirection * state.a11yStep).coerceIn( (value + targetDirection * state.step).coerceIn( state.valueRange.start, state.valueRange.endInclusive, ) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt +8 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel import android.bluetooth.BluetoothDevice import android.graphics.drawable.TestStubDrawable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.CachedBluetoothDevice Loading Loading @@ -63,6 +64,12 @@ class AudioSharingStreamSliderViewModelTest : SysuiTestCase() { assertThat(audioSharingSlider!!.label).isEqualTo("my headset 2") assertThat(audioSharingSlider!!.icon) .isEqualTo(Icon.Resource(R.drawable.ic_volume_media_bt, null)) .isEqualTo( Icon.Loaded( drawable = TestStubDrawable(), res = R.drawable.ic_volume_media_bt, contentDescription = null, ) ) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt +8 −4 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel import android.app.Flags import android.app.NotificationManager.INTERRUPTION_FILTER_NONE import android.graphics.drawable.TestStubDrawable import android.media.AudioManager import android.platform.test.annotations.EnableFlags import android.service.notification.ZenPolicy Loading @@ -28,7 +29,6 @@ import com.android.settingslib.notification.modes.TestModeBuilder import com.android.settingslib.volume.shared.model.AudioStream import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.collectLastValue Loading @@ -39,8 +39,6 @@ import com.android.systemui.statusbar.policy.data.repository.fakeZenModeReposito import com.android.systemui.testKosmos import com.android.systemui.volume.data.repository.audioSharingRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock Loading Loading @@ -173,6 +171,12 @@ class AudioStreamSliderViewModelTest : SysuiTestCase() { assertThat(mediaSlider!!.label).isEqualTo("my headset 1") assertThat(mediaSlider!!.icon) .isEqualTo(Icon.Resource(R.drawable.ic_volume_media_bt, null)) .isEqualTo( Icon.Loaded( drawable = TestStubDrawable(), res = R.drawable.ic_volume_media_bt, contentDescription = null, ) ) } }
packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt +32 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.common.shared.model import android.annotation.DrawableRes import android.graphics.drawable.Drawable import androidx.compose.runtime.Stable import com.android.systemui.common.shared.model.Icon.Loaded /** * Models an icon, that can either be already [loaded][Icon.Loaded] or be a [reference] Loading @@ -33,8 +34,37 @@ sealed class Icon { constructor( val drawable: Drawable, override val contentDescription: ContentDescription?, /** * Serves as an id to compare two instances. When provided this is used alongside * [contentDescription] to determine equality. This is useful when comparing icons * representing the same UI, but with different [drawable] instances. */ @DrawableRes val res: Int? = null, ) : Icon() ) : Icon() { override fun equals(other: Any?): Boolean { val that = other as? Loaded ?: return false if (this.res != null && that.res != null) { return this.res == that.res && this.contentDescription == that.contentDescription } return this.res == that.res && this.drawable == that.drawable && this.contentDescription == that.contentDescription } override fun hashCode(): Int { var result = contentDescription?.hashCode() ?: 0 result = if (res != null) { 31 * result + res.hashCode() } else { 31 * result + drawable.hashCode() } return result } } data class Resource( @DrawableRes val res: Int, Loading @@ -49,4 +79,4 @@ sealed class Icon { fun Drawable.asIcon( contentDescription: ContentDescription? = null, @DrawableRes res: Int? = null, ): Icon.Loaded = Icon.Loaded(this, contentDescription, res) ): Loaded = Loaded(this, contentDescription, res)
packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt +5 −6 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.systemui.volume.dialog.sliders.ui import android.graphics.drawable.Drawable import android.view.View import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.tween Loading @@ -29,7 +28,6 @@ import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SliderDefaults import androidx.compose.runtime.Composable Loading @@ -43,7 +41,8 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.theme.PlatformTheme import com.android.compose.ui.graphics.painter.DrawablePainter import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.res.R Loading Loading @@ -155,7 +154,7 @@ private fun VolumeDialogSlider( }, ) }, accessibilityParams = AccessibilityParams(label = sliderStateModel.label), accessibilityParams = AccessibilityParams(contentDescription = sliderStateModel.label), modifier = modifier.pointerInput(Unit) { coroutineScope { Loading @@ -172,7 +171,7 @@ private fun VolumeDialogSlider( @Composable private fun BoxScope.VolumeIcon( drawable: Drawable, icon: Icon.Loaded, isVisible: Boolean, modifier: Modifier = Modifier, ) { Loading @@ -182,6 +181,6 @@ private fun BoxScope.VolumeIcon( exit = fadeOut(animationSpec = tween(durationMillis = 50)), modifier = modifier.align(Alignment.Center).size(40.dp).padding(10.dp), ) { Icon(painter = DrawablePainter(drawable), contentDescription = null) Icon(icon) } }