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

Commit d0accbb6 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Listen for playback state changes" into rvc-dev am: 615aa632 am: 6d76d93b

Change-Id: I8b015760877ba81466c04d348fd3455245490eb6
parents 041b083f 6d76d93b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -147,6 +147,7 @@ public class MediaControlPanel {
        if (mSeekBarObserver != null) {
            mSeekBarViewModel.getProgress().removeObserver(mSeekBarObserver);
        }
        mSeekBarViewModel.onDestroy();
    }

    private void loadDimens() {
+30 −1
Original line number Diff line number Diff line
@@ -78,7 +78,22 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
    val progress: LiveData<Progress>
        get() = _progress
    private var controller: MediaController? = null
        set(value) {
            if (field?.sessionToken != value?.sessionToken) {
                field?.unregisterCallback(callback)
                value?.registerCallback(callback)
                field = value
            }
        }
    private var playbackState: PlaybackState? = null
    private var callback = object : MediaController.Callback() {
        override fun onPlaybackStateChanged(state: PlaybackState) {
            playbackState = state
            if (shouldPollPlaybackPosition()) {
                checkPlaybackPosition()
            }
        }
    }

    /** Listening state (QS open or closed) is used to control polling of progress. */
    var listening = true
@@ -95,6 +110,9 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
    @WorkerThread
    fun onSeek(position: Long) {
        controller?.transportControls?.seekTo(position)
        // Invalidate the cached playbackState to avoid the thumb jumping back to the previous
        // position.
        playbackState = null
    }

    /**
@@ -125,12 +143,23 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
     */
    @AnyThread
    fun clearController() = bgExecutor.execute {
        controller = null
        playbackState = null
        _data = _data.copy(enabled = false)
    }

    /**
     * Call to clean up any resources.
     */
    @AnyThread
    fun onDestroy() {
        controller = null
        playbackState = null
    }

    @AnyThread
    private fun checkPlaybackPosition(): Runnable = bgExecutor.executeDelayed({
        val duration = _data?.duration ?: -1
        val duration = _data.duration ?: -1
        val currentPosition = playbackState?.computePosition(duration.toLong())?.toInt()
        if (currentPosition != null && _data.elapsedTime != currentPosition) {
            _data = _data.copy(elapsedTime = currentPosition)
+79 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.media

import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -35,9 +36,12 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever

@@ -61,12 +65,15 @@ public class SeekBarViewModelTest : SysuiTestCase() {
    }
    @Mock private lateinit var mockController: MediaController
    @Mock private lateinit var mockTransport: MediaController.TransportControls
    private val token1 = MediaSession.Token(1, null)
    private val token2 = MediaSession.Token(2, null)

    @Before
    fun setUp() {
        fakeExecutor = FakeExecutor(FakeSystemClock())
        viewModel = SeekBarViewModel(fakeExecutor)
        mockController = mock(MediaController::class.java)
        whenever(mockController.sessionToken).thenReturn(token1)
        mockTransport = mock(MediaController.TransportControls::class.java)

        // LiveData to run synchronously
@@ -78,6 +85,42 @@ public class SeekBarViewModelTest : SysuiTestCase() {
        ArchTaskExecutor.getInstance().setDelegate(null)
    }

    @Test
    fun updateRegistersCallback() {
        viewModel.updateController(mockController)
        verify(mockController).registerCallback(any())
    }

    @Test
    fun updateSecondTimeDoesNotRepeatRegistration() {
        viewModel.updateController(mockController)
        viewModel.updateController(mockController)
        verify(mockController, times(1)).registerCallback(any())
    }

    @Test
    fun updateDifferentControllerUnregistersCallback() {
        viewModel.updateController(mockController)
        viewModel.updateController(mock(MediaController::class.java))
        verify(mockController).unregisterCallback(any())
    }

    @Test
    fun updateDifferentControllerRegistersCallback() {
        viewModel.updateController(mockController)
        val controller2 = mock(MediaController::class.java)
        whenever(controller2.sessionToken).thenReturn(token2)
        viewModel.updateController(controller2)
        verify(controller2).registerCallback(any())
    }

    @Test
    fun updateToNullUnregistersCallback() {
        viewModel.updateController(mockController)
        viewModel.updateController(null)
        verify(mockController).unregisterCallback(any())
    }

    @Test
    fun updateDurationWithPlayback() {
        // GIVEN that the duration is contained within the metadata
@@ -374,6 +417,26 @@ public class SeekBarViewModelTest : SysuiTestCase() {
        assertThat(fakeExecutor.numPending()).isEqualTo(1)
    }

    @Test
    fun playbackChangeQueuesPollTask() {
        viewModel.updateController(mockController)
        val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
        verify(mockController).registerCallback(captor.capture())
        val callback = captor.value
        // WHEN the callback receives an new state
        val state = PlaybackState.Builder().run {
            setState(PlaybackState.STATE_PLAYING, 100L, 1f)
            build()
        }
        callback.onPlaybackStateChanged(state)
        with(fakeExecutor) {
            advanceClockToNext()
            runAllReady()
        }
        // THEN an update task is queued
        assertThat(fakeExecutor.numPending()).isEqualTo(1)
    }

    @Test
    fun clearSeekBar() {
        // GIVEN that the duration is contained within the metadata
@@ -399,4 +462,20 @@ public class SeekBarViewModelTest : SysuiTestCase() {
        // THEN the seek bar is disabled
        assertThat(viewModel.progress.value!!.enabled).isFalse()
    }

    @Test
    fun clearSeekBarUnregistersCallback() {
        viewModel.updateController(mockController)
        viewModel.clearController()
        fakeExecutor.runAllReady()
        verify(mockController).unregisterCallback(any())
    }

    @Test
    fun destroyUnregistersCallback() {
        viewModel.updateController(mockController)
        viewModel.onDestroy()
        fakeExecutor.runAllReady()
        verify(mockController).unregisterCallback(any())
    }
}