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

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

Merge "Update media progress bar content description" into main

parents c0b844b8 24df7560
Loading
Loading
Loading
Loading
+28 −7
Original line number Original line Diff line number Diff line
@@ -18,6 +18,9 @@ package com.android.systemui.media.controls.ui.binder


import android.animation.Animator
import android.animation.Animator
import android.animation.ObjectAnimator
import android.animation.ObjectAnimator
import android.icu.text.MeasureFormat
import android.icu.util.Measure
import android.icu.util.MeasureUnit
import android.testing.TestableLooper
import android.testing.TestableLooper
import android.view.View
import android.view.View
import android.widget.SeekBar
import android.widget.SeekBar
@@ -30,6 +33,7 @@ import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import java.util.Locale
import org.junit.Before
import org.junit.Before
import org.junit.Rule
import org.junit.Rule
import org.junit.Test
import org.junit.Test
@@ -61,11 +65,11 @@ class SeekBarObserverTest : SysuiTestCase() {
    fun setUp() {
    fun setUp() {
        context.orCreateTestableResources.addOverride(
        context.orCreateTestableResources.addOverride(
            R.dimen.qs_media_enabled_seekbar_height,
            R.dimen.qs_media_enabled_seekbar_height,
            enabledHeight
            enabledHeight,
        )
        )
        context.orCreateTestableResources.addOverride(
        context.orCreateTestableResources.addOverride(
            R.dimen.qs_media_disabled_seekbar_height,
            R.dimen.qs_media_disabled_seekbar_height,
            disabledHeight
            disabledHeight,
        )
        )


        seekBarView = SeekBar(context)
        seekBarView = SeekBar(context)
@@ -110,14 +114,31 @@ class SeekBarObserverTest : SysuiTestCase() {


    @Test
    @Test
    fun seekBarProgress() {
    fun seekBarProgress() {
        val elapsedTime = 3000
        val duration = (1.5 * 60 * 60 * 1000).toInt()
        // WHEN part of the track has been played
        // WHEN part of the track has been played
        val data = SeekBarViewModel.Progress(true, true, true, false, 3000, 120000, true)
        val data = SeekBarViewModel.Progress(true, true, true, false, elapsedTime, duration, true)
        observer.onChanged(data)
        observer.onChanged(data)
        // THEN seek bar shows the progress
        // THEN seek bar shows the progress
        assertThat(seekBarView.progress).isEqualTo(3000)
        assertThat(seekBarView.progress).isEqualTo(elapsedTime)
        assertThat(seekBarView.max).isEqualTo(120000)
        assertThat(seekBarView.max).isEqualTo(duration)


        val desc = context.getString(R.string.controls_media_seekbar_description, "00:03", "02:00")
        val expectedProgress =
            MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
                .formatMeasures(Measure(3, MeasureUnit.SECOND))
        val expectedDuration =
            MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
                .formatMeasures(
                    Measure(1, MeasureUnit.HOUR),
                    Measure(30, MeasureUnit.MINUTE),
                    Measure(0, MeasureUnit.SECOND),
                )
        val desc =
            context.getString(
                R.string.controls_media_seekbar_description,
                expectedProgress,
                expectedDuration,
            )
        assertThat(seekBarView.contentDescription).isEqualTo(desc)
        assertThat(seekBarView.contentDescription).isEqualTo(desc)
    }
    }


+2 −2
Original line number Original line Diff line number Diff line
@@ -3170,8 +3170,8 @@
    <string name="controls_media_settings_button">Settings</string>
    <string name="controls_media_settings_button">Settings</string>
    <!-- Description for media control's playing media item, including information for the media's title, the artist, and source app [CHAR LIMIT=NONE]-->
    <!-- Description for media control's playing media item, including information for the media's title, the artist, and source app [CHAR LIMIT=NONE]-->
    <string name="controls_media_playing_item_description"><xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> by <xliff:g id="artist_name" example="Various artists">%2$s</xliff:g> is playing from <xliff:g id="app_label" example="Spotify">%3$s</xliff:g></string>
    <string name="controls_media_playing_item_description"><xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> by <xliff:g id="artist_name" example="Various artists">%2$s</xliff:g> is playing from <xliff:g id="app_label" example="Spotify">%3$s</xliff:g></string>
    <!-- Content description for media cotnrols progress bar [CHAR_LIMIT=NONE] -->
    <!-- Content description for media controls progress bar [CHAR_LIMIT=NONE] -->
    <string name="controls_media_seekbar_description"><xliff:g id="elapsed_time" example="1:30">%1$s</xliff:g> of <xliff:g id="total_time" example="3:00">%2$s</xliff:g></string>
    <string name="controls_media_seekbar_description"><xliff:g id="elapsed_time" example="1 hour 2 minutes 30 seconds">%1$s</xliff:g> of <xliff:g id="total_time" example="4 hours 5 seconds">%2$s</xliff:g></string>
    <!-- Placeholder title to inform user that an app has posted media controls [CHAR_LIMIT=NONE] -->
    <!-- Placeholder title to inform user that an app has posted media controls [CHAR_LIMIT=NONE] -->
    <string name="controls_media_empty_title"><xliff:g id="app_name" example="Foo Music App">%1$s</xliff:g> is running</string>
    <string name="controls_media_empty_title"><xliff:g id="app_name" example="Foo Music App">%1$s</xliff:g> is running</string>


+55 −8
Original line number Original line Diff line number Diff line
@@ -18,6 +18,9 @@ package com.android.systemui.media.controls.ui.binder


import android.animation.Animator
import android.animation.Animator
import android.animation.ObjectAnimator
import android.animation.ObjectAnimator
import android.icu.text.MeasureFormat
import android.icu.util.Measure
import android.icu.util.MeasureUnit
import android.text.format.DateUtils
import android.text.format.DateUtils
import androidx.annotation.UiThread
import androidx.annotation.UiThread
import androidx.lifecycle.Observer
import androidx.lifecycle.Observer
@@ -28,8 +31,11 @@ import com.android.systemui.media.controls.ui.drawable.SquigglyProgress
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
import com.android.systemui.res.R
import java.util.Locale


private const val TAG = "SeekBarObserver"
private const val TAG = "SeekBarObserver"
private const val MIN_IN_SEC = 60
private const val HOUR_IN_SEC = MIN_IN_SEC * 60


/**
/**
 * Observer for changes from SeekBarViewModel.
 * Observer for changes from SeekBarViewModel.
@@ -127,10 +133,9 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
        }
        }


        holder.seekBar.setMax(data.duration)
        holder.seekBar.setMax(data.duration)
        val totalTimeString =
        val totalTimeDescription = formatTimeContentDescription(data.duration)
            DateUtils.formatElapsedTime(data.duration / DateUtils.SECOND_IN_MILLIS)
        if (data.scrubbing) {
        if (data.scrubbing) {
            holder.scrubbingTotalTimeView.text = totalTimeString
            holder.scrubbingTotalTimeView.text = formatTimeLabel(data.duration)
        }
        }


        data.elapsedTime?.let {
        data.elapsedTime?.let {
@@ -148,20 +153,62 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
                }
                }
            }
            }


            val elapsedTimeString = DateUtils.formatElapsedTime(it / DateUtils.SECOND_IN_MILLIS)
            val elapsedTimeDescription = formatTimeContentDescription(it)
            if (data.scrubbing) {
            if (data.scrubbing) {
                holder.scrubbingElapsedTimeView.text = elapsedTimeString
                holder.scrubbingElapsedTimeView.text = formatTimeLabel(it)
            }
            }


            holder.seekBar.contentDescription =
            holder.seekBar.contentDescription =
                holder.seekBar.context.getString(
                holder.seekBar.context.getString(
                    R.string.controls_media_seekbar_description,
                    R.string.controls_media_seekbar_description,
                    elapsedTimeString,
                    elapsedTimeDescription,
                    totalTimeString
                    totalTimeDescription,
                )
                )
        }
        }
    }
    }


    /** Returns a time string suitable for display, e.g. "12:34" */
    private fun formatTimeLabel(milliseconds: Int): CharSequence {
        return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
    }

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

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

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

        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())
    }

    @VisibleForTesting
    @VisibleForTesting
    open fun buildResetAnimator(targetTime: Int): Animator {
    open fun buildResetAnimator(targetTime: Int): Animator {
        val animator =
        val animator =
@@ -169,7 +216,7 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
                holder.seekBar,
                holder.seekBar,
                "progress",
                "progress",
                holder.seekBar.progress,
                holder.seekBar.progress,
                targetTime + RESET_ANIMATION_DURATION_MS
                targetTime + RESET_ANIMATION_DURATION_MS,
            )
            )
        animator.setAutoCancel(true)
        animator.setAutoCancel(true)
        animator.duration = RESET_ANIMATION_DURATION_MS.toLong()
        animator.duration = RESET_ANIMATION_DURATION_MS.toLong()