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

Commit 8e6fbbe4 authored by Beth Thibodeau's avatar Beth Thibodeau
Browse files

Use listener to update seekbar description async

Because multiple sources update the progress data, there can be race
events between the initial update and when the description updated. This
decouples those and uses a listener pattern to update the content
description separately from the progress state.

Fixes: 399825527
Flag: EXEMPT bugfix
Test: atest SeekBarViewModelTest
Test: manual - with and without scene container flag
Change-Id: I579bbcaadd825277d82dfb7fd4fd2cef3d870c1a
parent 280cfec4
Loading
Loading
Loading
Loading
+12 −9
Original line number Diff line number Diff line
@@ -127,7 +127,6 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
        }

        holder.seekBar.setMax(data.duration)
        val totalTimeDescription = data.durationDescription
        if (data.scrubbing) {
            holder.scrubbingTotalTimeView.text = formatTimeLabel(data.duration)
        }
@@ -147,17 +146,9 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
                }
            }

            val elapsedTimeDescription = data.elapsedTimeDescription
            if (data.scrubbing) {
                holder.scrubbingElapsedTimeView.text = formatTimeLabel(it)
            }

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

@@ -166,6 +157,18 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
        return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
    }

    fun updateContentDescription(
        elapsedTimeDescription: CharSequence,
        durationDescription: CharSequence,
    ) {
        holder.seekBar.contentDescription =
            holder.seekBar.context.getString(
                R.string.controls_media_seekbar_description,
                elapsedTimeDescription,
                durationDescription,
            )
    }

    @VisibleForTesting
    open fun buildResetAnimator(targetTime: Int): Animator {
        val animator =
+8 −0
Original line number Diff line number Diff line
@@ -224,6 +224,8 @@ public class MediaControlPanel {
            this::setIsScrubbing;
    private final SeekBarViewModel.EnabledChangeListener mEnabledChangeListener =
            this::setIsSeekBarEnabled;
    private final SeekBarViewModel.ContentDescriptionListener mContentDescriptionListener =
            this::setSeekbarContentDescription;

    private final BroadcastDialogController mBroadcastDialogController;
    private boolean mIsCurrentBroadcastedApp = false;
@@ -327,6 +329,7 @@ public class MediaControlPanel {
        }
        mSeekBarViewModel.removeScrubbingChangeListener(mScrubbingChangeListener);
        mSeekBarViewModel.removeEnabledChangeListener(mEnabledChangeListener);
        mSeekBarViewModel.removeContentDescriptionListener(mContentDescriptionListener);
        mSeekBarViewModel.onDestroy();
        mMediaViewController.onDestroy();
    }
@@ -395,6 +398,10 @@ public class MediaControlPanel {
        });
    }

    private void setSeekbarContentDescription(CharSequence elapsedTime, CharSequence duration) {
        mSeekBarObserver.updateContentDescription(elapsedTime, duration);
    }

    /**
     * Reloads animator duration scale.
     */
@@ -424,6 +431,7 @@ public class MediaControlPanel {
        mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
        mSeekBarViewModel.setScrubbingChangeListener(mScrubbingChangeListener);
        mSeekBarViewModel.setEnabledChangeListener(mEnabledChangeListener);
        mSeekBarViewModel.setContentDescriptionListener(mContentDescriptionListener);
        mMediaViewController.attach(player);

        vh.getPlayer().setOnLongClickListener(v -> {
+16 −0
Original line number Diff line number Diff line
@@ -229,6 +229,20 @@ constructor(
            }
        }

    private val seekbarDescriptionListener =
        object : SeekBarViewModel.ContentDescriptionListener {
            override fun onContentDescriptionChanged(
                elapsedTimeDescription: CharSequence,
                durationDescription: CharSequence,
            ) {
                if (!SceneContainerFlag.isEnabled) return
                seekBarObserver.updateContentDescription(
                    elapsedTimeDescription,
                    durationDescription,
                )
            }
        }

    /**
     * Sets the listening state of the player.
     *
@@ -350,6 +364,7 @@ constructor(
            }
            seekBarViewModel.removeScrubbingChangeListener(scrubbingChangeListener)
            seekBarViewModel.removeEnabledChangeListener(enabledChangeListener)
            seekBarViewModel.removeContentDescriptionListener(seekbarDescriptionListener)
            seekBarViewModel.onDestroy()
        }
        mediaHostStatesManager.removeController(this)
@@ -653,6 +668,7 @@ constructor(
        seekBarViewModel.attachTouchHandlers(mediaViewHolder.seekBar)
        seekBarViewModel.setScrubbingChangeListener(scrubbingChangeListener)
        seekBarViewModel.setEnabledChangeListener(enabledChangeListener)
        seekBarViewModel.setContentDescriptionListener(seekbarDescriptionListener)

        val mediaCard = mediaViewHolder.player
        attach(mediaViewHolder.player)
+25 −8
Original line number Diff line number Diff line
@@ -104,19 +104,20 @@ constructor(
        )
        set(value) {
            val enabledChanged = value.enabled != field.enabled
            field = value
            if (enabledChanged) {
                enabledChangeListener?.onEnabledChanged(value.enabled)
            }
            _progress.postValue(value)

            bgExecutor.execute {
                val durationDescription = formatTimeContentDescription(value.duration)
                val elapsedDescription =
                    value.elapsedTime?.let { formatTimeContentDescription(it) } ?: ""
                field =
                    value.copy(
                        durationDescription = durationDescription,
                        elapsedTimeDescription = elapsedDescription,
                contentDescriptionListener?.onContentDescriptionChanged(
                    elapsedDescription,
                    durationDescription,
                )
                _progress.postValue(field)
            }
        }

@@ -175,6 +176,7 @@ constructor(

    private var scrubbingChangeListener: ScrubbingChangeListener? = null
    private var enabledChangeListener: EnabledChangeListener? = null
    private var contentDescriptionListener: ContentDescriptionListener? = null

    /** Set to true when the user is touching the seek bar to change the position. */
    private var scrubbing = false
@@ -394,6 +396,16 @@ constructor(
        }
    }

    fun setContentDescriptionListener(listener: ContentDescriptionListener) {
        contentDescriptionListener = listener
    }

    fun removeContentDescriptionListener(listener: ContentDescriptionListener) {
        if (listener == contentDescriptionListener) {
            contentDescriptionListener = null
        }
    }

    /** returns a pair of whether seekbar is enabled and the duration of media. */
    private fun getEnabledStateAndDuration(metadata: MediaMetadata?): Pair<Boolean, Int> {
        val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
@@ -468,6 +480,13 @@ constructor(
        fun onEnabledChanged(enabled: Boolean)
    }

    interface ContentDescriptionListener {
        fun onContentDescriptionChanged(
            elapsedTimeDescription: CharSequence,
            durationDescription: CharSequence,
        )
    }

    private class SeekBarChangeListener(
        val viewModel: SeekBarViewModel,
        val falsingManager: FalsingManager,
@@ -639,7 +658,5 @@ constructor(
        val duration: Int,
        /** whether seekBar is listening to progress updates */
        val listening: Boolean,
        val elapsedTimeDescription: CharSequence = "",
        val durationDescription: CharSequence = "",
    )
}
+17 −5
Original line number Diff line number Diff line
@@ -855,6 +855,20 @@ public class SeekBarViewModelTest : SysuiTestCase() {

    @Test
    fun contentDescriptionUpdated() {
        var elapsedTimeDesc: CharSequence? = null
        var durationDesc: CharSequence? = null
        val listener =
            object : SeekBarViewModel.ContentDescriptionListener {
                override fun onContentDescriptionChanged(
                    elapsedTimeDescription: CharSequence,
                    durationDescription: CharSequence,
                ) {
                    elapsedTimeDesc = elapsedTimeDescription
                    durationDesc = durationDescription
                }
            }
        viewModel.setContentDescriptionListener(listener)

        // When there is a duration and position
        val duration = (1.5 * 60 * 60 * 1000).toLong()
        val metadata =
@@ -875,9 +889,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
        viewModel.updateController(mockController)
        fakeExecutor.runNextReady()

        // Then the content description is set
        val result = viewModel.progress.value!!

        // Then the content description listener gets an update
        val expectedProgress =
            MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
                .formatMeasures(Measure(3, MeasureUnit.SECOND))
@@ -888,7 +900,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
                    Measure(30, MeasureUnit.MINUTE),
                    Measure(0, MeasureUnit.SECOND),
                )
        assertThat(result.durationDescription).isEqualTo(expectedDuration)
        assertThat(result.elapsedTimeDescription).isEqualTo(expectedProgress)
        assertThat(elapsedTimeDesc).isEqualTo(expectedProgress)
        assertThat(durationDesc).isEqualTo(expectedDuration)
    }
}