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

Commit 54d27f03 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[Media] A11y string for seekbar." into main

parents 927a8853 f20414a2
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -98,6 +98,8 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
@@ -678,7 +680,10 @@ private fun ContentScope.Navigation(
                                    modifier = Modifier.fillMaxWidth(),
                                )
                            },
                            modifier = Modifier.fillMaxWidth(),
                            modifier =
                                Modifier.fillMaxWidth().clearAndSetSemantics {
                                    contentDescription = viewModel.contentDescription
                                },
                        )
                    }
                }
+2 −0
Original line number Diff line number Diff line
@@ -59,6 +59,8 @@ sealed interface MediaNavigationViewModel {
         * the seek bar). The position/progress should be committed.
         */
        val onScrubFinished: () -> Unit,
        /** Accessibility string to attach to the seekbar UI element. */
        val contentDescription: String,
    ) : MediaNavigationViewModel

    /** The seek bar should be hidden. */
+53 −0
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
package com.android.systemui.media.remedia.ui.viewmodel

import android.content.Context
import android.icu.text.MeasureFormat
import android.icu.util.Measure
import android.icu.util.MeasureUnit
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
@@ -38,7 +41,9 @@ import com.android.systemui.res.R
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.util.Locale
import kotlin.math.roundToLong
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.awaitCancellation

/** Models UI state for a media element. */
@@ -118,6 +123,12 @@ constructor(
                                    }
                                    isScrubbing = false
                                },
                                contentDescription =
                                    context.getString(
                                        R.string.controls_media_seekbar_description,
                                        formatTimeContentDescription(session.positionMs),
                                        formatTimeContentDescription(session.durationMs),
                                    ),
                            )
                        } else {
                            MediaNavigationViewModel.Hidden
@@ -298,6 +309,43 @@ constructor(
        }
    }

    /**
     * Returns a time string suitable for content description, e.g. "12 minutes 34 seconds"
     *
     * Follows same logic as Chronometer#formatDuration
     */
    private fun formatTimeContentDescription(milliseconds: Long): String {
        var seconds = milliseconds.milliseconds.inWholeSeconds

        val hours =
            if (seconds >= OneHourInSec) {
                seconds / OneHourInSec
            } else {
                0
            }
        seconds -= hours * OneHourInSec

        val minutes =
            if (seconds >= OneMinuteInSec) {
                seconds / OneMinuteInSec
            } else {
                0
            }
        seconds -= minutes * OneMinuteInSec

        val measures = arrayListOf<Measure>()
        if (hours > 0) {
            measures.add(Measure(hours, MeasureUnit.HOUR))
        }
        if (minutes > 0) {
            measures.add(Measure(minutes, MeasureUnit.MINUTE))
        }
        measures.add(Measure(seconds, MeasureUnit.SECOND))

        return MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
            .formatMeasures(*measures.toTypedArray())
    }

    interface FalsingSystem {
        fun runIfNotFalseTap(@FalsingManager.Penalty penalty: Int, block: () -> Unit)

@@ -308,4 +356,9 @@ constructor(
    interface Factory {
        fun create(context: Context, carouselVisibility: MediaCarouselVisibility): MediaViewModel
    }

    companion object {
        private const val OneMinuteInSec = 60
        private const val OneHourInSec = OneMinuteInSec * 60
    }
}