Loading packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +8 −4 Original line number Original line Diff line number Diff line Loading @@ -49,15 +49,17 @@ import com.android.settingslib.Utils; import com.android.settingslib.media.MediaOutputSliceConstants; import com.android.settingslib.media.MediaOutputSliceConstants; import com.android.settingslib.widget.AdaptiveIcon; import com.android.settingslib.widget.AdaptiveIcon; import com.android.systemui.R; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.util.animation.TransitionLayout; import com.android.systemui.util.animation.TransitionLayout; import com.android.systemui.util.concurrency.DelayableExecutor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executor; import javax.inject.Inject; /** /** * A view controller used for Media Playback. * A view controller used for Media Playback. */ */ Loading Loading @@ -93,12 +95,14 @@ public class MediaControlPanel { * @param backgroundExecutor background executor, used for processing artwork * @param backgroundExecutor background executor, used for processing artwork * @param activityStarter activity starter * @param activityStarter activity starter */ */ public MediaControlPanel(Context context, DelayableExecutor backgroundExecutor, @Inject ActivityStarter activityStarter, MediaHostStatesManager mediaHostStatesManager) { public MediaControlPanel(Context context, @Background Executor backgroundExecutor, ActivityStarter activityStarter, MediaHostStatesManager mediaHostStatesManager, SeekBarViewModel seekBarViewModel) { mContext = context; mContext = context; mBackgroundExecutor = backgroundExecutor; mBackgroundExecutor = backgroundExecutor; mActivityStarter = activityStarter; mActivityStarter = activityStarter; mSeekBarViewModel = new SeekBarViewModel(backgroundExecutor); mSeekBarViewModel = seekBarViewModel; mMediaViewController = new MediaViewController(context, mediaHostStatesManager); mMediaViewController = new MediaViewController(context, mediaHostStatesManager); loadDimens(); loadDimens(); } } Loading packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt +4 −8 Original line number Original line Diff line number Diff line Loading @@ -11,14 +11,12 @@ import android.widget.HorizontalScrollView import android.widget.LinearLayout import android.widget.LinearLayout import androidx.core.view.GestureDetectorCompat import androidx.core.view.GestureDetectorCompat import com.android.systemui.R import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.plugins.ActivityStarter import com.android.systemui.qs.PageIndicator import com.android.systemui.qs.PageIndicator import com.android.systemui.statusbar.notification.VisualStabilityManager import com.android.systemui.statusbar.notification.VisualStabilityManager import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.requiresRemeasuring import com.android.systemui.util.animation.requiresRemeasuring import com.android.systemui.util.concurrency.DelayableExecutor import javax.inject.Inject import javax.inject.Inject import javax.inject.Provider import javax.inject.Singleton import javax.inject.Singleton private const val FLING_SLOP = 1000000 private const val FLING_SLOP = 1000000 Loading @@ -30,9 +28,8 @@ private const val FLING_SLOP = 1000000 @Singleton @Singleton class MediaViewManager @Inject constructor( class MediaViewManager @Inject constructor( private val context: Context, private val context: Context, @Background private val backgroundExecutor: DelayableExecutor, private val mediaControlPanelFactory: Provider<MediaControlPanel>, private val visualStabilityManager: VisualStabilityManager, private val visualStabilityManager: VisualStabilityManager, private val activityStarter: ActivityStarter, private val mediaHostStatesManager: MediaHostStatesManager, private val mediaHostStatesManager: MediaHostStatesManager, mediaManager: MediaDataCombineLatest mediaManager: MediaDataCombineLatest ) { ) { Loading Loading @@ -265,8 +262,7 @@ class MediaViewManager @Inject constructor( } } var existingPlayer = mediaPlayers[key] var existingPlayer = mediaPlayers[key] if (existingPlayer == null) { if (existingPlayer == null) { existingPlayer = MediaControlPanel(context, backgroundExecutor, activityStarter, existingPlayer = mediaControlPanelFactory.get() mediaHostStatesManager) existingPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), existingPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), mediaContent)) mediaContent)) mediaPlayers[key] = existingPlayer mediaPlayers[key] = existingPlayer Loading @@ -289,7 +285,7 @@ class MediaViewManager @Inject constructor( needsReordering = true needsReordering = true } } } } existingPlayer.bind(data) existingPlayer?.bind(data) updateMediaPaddings() updateMediaPaddings() updatePageIndicator() updatePageIndicator() } } Loading packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt +34 −22 Original line number Original line Diff line number Diff line Loading @@ -27,8 +27,10 @@ import androidx.annotation.AnyThread import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.concurrency.RepeatableExecutor import java.util.concurrent.Executor import javax.inject.Inject private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L Loading Loading @@ -65,7 +67,7 @@ private fun PlaybackState.computePosition(duration: Long): Long { } } /** ViewModel for seek bar in QS media player. */ /** ViewModel for seek bar in QS media player. */ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: RepeatableExecutor) { private var _data = Progress(false, false, null, null) private var _data = Progress(false, false, null, null) set(value) { set(value) { Loading @@ -89,10 +91,10 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { private var callback = object : MediaController.Callback() { private var callback = object : MediaController.Callback() { override fun onPlaybackStateChanged(state: PlaybackState) { override fun onPlaybackStateChanged(state: PlaybackState) { playbackState = state playbackState = state if (shouldPollPlaybackPosition()) { if (PlaybackState.STATE_NONE.equals(playbackState)) { checkPlaybackPosition() } else if (PlaybackState.STATE_NONE.equals(playbackState)) { clearController() clearController() } else { checkIfPollingNeeded() } } } } Loading @@ -100,12 +102,14 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { clearController() clearController() } } } } private var cancel: Runnable? = null /** Listening state (QS open or closed) is used to control polling of progress. */ /** Listening state (QS open or closed) is used to control polling of progress. */ var listening = true var listening = true set(value) { set(value) = bgExecutor.execute { if (value) { if (field != value) { checkPlaybackPosition() field = value checkIfPollingNeeded() } } } } Loading Loading @@ -137,9 +141,7 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { playbackState?.getState() == PlaybackState.STATE_NONE || playbackState?.getState() == PlaybackState.STATE_NONE || (duration != null && duration <= 0)) false else true (duration != null && duration <= 0)) false else true _data = Progress(enabled, seekAvailable, position, duration) _data = Progress(enabled, seekAvailable, position, duration) if (shouldPollPlaybackPosition()) { checkIfPollingNeeded() checkPlaybackPosition() } } } /** /** Loading @@ -151,6 +153,8 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { fun clearController() = bgExecutor.execute { fun clearController() = bgExecutor.execute { controller = null controller = null playbackState = null playbackState = null cancel?.run() cancel = null _data = _data.copy(enabled = false) _data = _data.copy(enabled = false) } } Loading @@ -158,26 +162,34 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { * Call to clean up any resources. * Call to clean up any resources. */ */ @AnyThread @AnyThread fun onDestroy() { fun onDestroy() = bgExecutor.execute { controller = null controller = null playbackState = null playbackState = null cancel?.run() cancel = null } } @AnyThread @WorkerThread private fun checkPlaybackPosition(): Runnable = bgExecutor.executeDelayed({ private fun checkPlaybackPosition() { val duration = _data.duration ?: -1 val duration = _data.duration ?: -1 val currentPosition = playbackState?.computePosition(duration.toLong())?.toInt() val currentPosition = playbackState?.computePosition(duration.toLong())?.toInt() if (currentPosition != null && _data.elapsedTime != currentPosition) { if (currentPosition != null && _data.elapsedTime != currentPosition) { _data = _data.copy(elapsedTime = currentPosition) _data = _data.copy(elapsedTime = currentPosition) } } if (shouldPollPlaybackPosition()) { checkPlaybackPosition() } } }, POSITION_UPDATE_INTERVAL_MILLIS) @WorkerThread @WorkerThread private fun shouldPollPlaybackPosition(): Boolean { private fun checkIfPollingNeeded() { return listening && playbackState?.isInMotion() ?: false val needed = listening && playbackState?.isInMotion() ?: false if (needed) { if (cancel == null) { cancel = bgExecutor.executeRepeatedly(this::checkPlaybackPosition, 0L, POSITION_UPDATE_INTERVAL_MILLIS) } } else { cancel?.run() cancel = null } } } /** Gets a listener to attach to the seek bar to handle seeking. */ /** Gets a listener to attach to the seek bar to handle seeking. */ Loading @@ -194,7 +206,7 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { private class SeekBarChangeListener( private class SeekBarChangeListener( val viewModel: SeekBarViewModel, val viewModel: SeekBarViewModel, val bgExecutor: DelayableExecutor val bgExecutor: Executor ) : SeekBar.OnSeekBarChangeListener { ) : SeekBar.OnSeekBarChangeListener { override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) { override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) { if (fromUser) { if (fromUser) { Loading packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +10 −6 Original line number Original line Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.widget.ImageButton import android.widget.ImageView import android.widget.ImageView import android.widget.SeekBar import android.widget.SeekBar import android.widget.TextView import android.widget.TextView import androidx.lifecycle.LiveData import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase Loading @@ -41,6 +42,7 @@ import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.After import org.junit.Before import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.Test import org.junit.runner.RunWith import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentCaptor Loading @@ -48,6 +50,7 @@ import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit private const val KEY = "TEST_KEY" private const val KEY = "TEST_KEY" private const val APP = "APP" private const val APP = "APP" Loading @@ -73,6 +76,8 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var holder: PlayerViewHolder @Mock private lateinit var holder: PlayerViewHolder @Mock private lateinit var view: TransitionLayout @Mock private lateinit var view: TransitionLayout @Mock private lateinit var mediaHostStatesManager: MediaHostStatesManager @Mock private lateinit var mediaHostStatesManager: MediaHostStatesManager @Mock private lateinit var seekBarViewModel: SeekBarViewModel @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress> private lateinit var appIcon: ImageView private lateinit var appIcon: ImageView private lateinit var appName: TextView private lateinit var appName: TextView private lateinit var albumView: ImageView private lateinit var albumView: ImageView Loading @@ -94,18 +99,17 @@ public class MediaControlPanelTest : SysuiTestCase() { private val device = MediaDeviceData(true, null, DEVICE_NAME) private val device = MediaDeviceData(true, null, DEVICE_NAME) private val disabledDevice = MediaDeviceData(false, null, null) private val disabledDevice = MediaDeviceData(false, null, null) @JvmField @Rule val mockito = MockitoJUnit.rule() @Before @Before fun setUp() { fun setUp() { bgExecutor = FakeExecutor(FakeSystemClock()) bgExecutor = FakeExecutor(FakeSystemClock()) activityStarter = mock(ActivityStarter::class.java) player = MediaControlPanel(context, bgExecutor, activityStarter, mediaHostStatesManager, mediaHostStatesManager = mock(MediaHostStatesManager::class.java) seekBarViewModel) whenever(seekBarViewModel.progress).thenReturn(seekBarData) player = MediaControlPanel(context, bgExecutor, activityStarter, mediaHostStatesManager) // Mock out a view holder for the player to attach to. // Mock out a view holder for the player to attach to. holder = mock(PlayerViewHolder::class.java) view = mock(TransitionLayout::class.java) whenever(holder.player).thenReturn(view) whenever(holder.player).thenReturn(view) appIcon = ImageView(context) appIcon = ImageView(context) whenever(holder.appIcon).thenReturn(appIcon) whenever(holder.appIcon).thenReturn(appIcon) Loading packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt +2 −1 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeRepeatableExecutor import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat Loading Loading @@ -71,7 +72,7 @@ public class SeekBarViewModelTest : SysuiTestCase() { @Before @Before fun setUp() { fun setUp() { fakeExecutor = FakeExecutor(FakeSystemClock()) fakeExecutor = FakeExecutor(FakeSystemClock()) viewModel = SeekBarViewModel(fakeExecutor) viewModel = SeekBarViewModel(FakeRepeatableExecutor(fakeExecutor)) mockController = mock(MediaController::class.java) mockController = mock(MediaController::class.java) whenever(mockController.sessionToken).thenReturn(token1) whenever(mockController.sessionToken).thenReturn(token1) mockTransport = mock(MediaController.TransportControls::class.java) mockTransport = mock(MediaController.TransportControls::class.java) Loading Loading
packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +8 −4 Original line number Original line Diff line number Diff line Loading @@ -49,15 +49,17 @@ import com.android.settingslib.Utils; import com.android.settingslib.media.MediaOutputSliceConstants; import com.android.settingslib.media.MediaOutputSliceConstants; import com.android.settingslib.widget.AdaptiveIcon; import com.android.settingslib.widget.AdaptiveIcon; import com.android.systemui.R; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.util.animation.TransitionLayout; import com.android.systemui.util.animation.TransitionLayout; import com.android.systemui.util.concurrency.DelayableExecutor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executor; import javax.inject.Inject; /** /** * A view controller used for Media Playback. * A view controller used for Media Playback. */ */ Loading Loading @@ -93,12 +95,14 @@ public class MediaControlPanel { * @param backgroundExecutor background executor, used for processing artwork * @param backgroundExecutor background executor, used for processing artwork * @param activityStarter activity starter * @param activityStarter activity starter */ */ public MediaControlPanel(Context context, DelayableExecutor backgroundExecutor, @Inject ActivityStarter activityStarter, MediaHostStatesManager mediaHostStatesManager) { public MediaControlPanel(Context context, @Background Executor backgroundExecutor, ActivityStarter activityStarter, MediaHostStatesManager mediaHostStatesManager, SeekBarViewModel seekBarViewModel) { mContext = context; mContext = context; mBackgroundExecutor = backgroundExecutor; mBackgroundExecutor = backgroundExecutor; mActivityStarter = activityStarter; mActivityStarter = activityStarter; mSeekBarViewModel = new SeekBarViewModel(backgroundExecutor); mSeekBarViewModel = seekBarViewModel; mMediaViewController = new MediaViewController(context, mediaHostStatesManager); mMediaViewController = new MediaViewController(context, mediaHostStatesManager); loadDimens(); loadDimens(); } } Loading
packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt +4 −8 Original line number Original line Diff line number Diff line Loading @@ -11,14 +11,12 @@ import android.widget.HorizontalScrollView import android.widget.LinearLayout import android.widget.LinearLayout import androidx.core.view.GestureDetectorCompat import androidx.core.view.GestureDetectorCompat import com.android.systemui.R import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.plugins.ActivityStarter import com.android.systemui.qs.PageIndicator import com.android.systemui.qs.PageIndicator import com.android.systemui.statusbar.notification.VisualStabilityManager import com.android.systemui.statusbar.notification.VisualStabilityManager import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.requiresRemeasuring import com.android.systemui.util.animation.requiresRemeasuring import com.android.systemui.util.concurrency.DelayableExecutor import javax.inject.Inject import javax.inject.Inject import javax.inject.Provider import javax.inject.Singleton import javax.inject.Singleton private const val FLING_SLOP = 1000000 private const val FLING_SLOP = 1000000 Loading @@ -30,9 +28,8 @@ private const val FLING_SLOP = 1000000 @Singleton @Singleton class MediaViewManager @Inject constructor( class MediaViewManager @Inject constructor( private val context: Context, private val context: Context, @Background private val backgroundExecutor: DelayableExecutor, private val mediaControlPanelFactory: Provider<MediaControlPanel>, private val visualStabilityManager: VisualStabilityManager, private val visualStabilityManager: VisualStabilityManager, private val activityStarter: ActivityStarter, private val mediaHostStatesManager: MediaHostStatesManager, private val mediaHostStatesManager: MediaHostStatesManager, mediaManager: MediaDataCombineLatest mediaManager: MediaDataCombineLatest ) { ) { Loading Loading @@ -265,8 +262,7 @@ class MediaViewManager @Inject constructor( } } var existingPlayer = mediaPlayers[key] var existingPlayer = mediaPlayers[key] if (existingPlayer == null) { if (existingPlayer == null) { existingPlayer = MediaControlPanel(context, backgroundExecutor, activityStarter, existingPlayer = mediaControlPanelFactory.get() mediaHostStatesManager) existingPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), existingPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), mediaContent)) mediaContent)) mediaPlayers[key] = existingPlayer mediaPlayers[key] = existingPlayer Loading @@ -289,7 +285,7 @@ class MediaViewManager @Inject constructor( needsReordering = true needsReordering = true } } } } existingPlayer.bind(data) existingPlayer?.bind(data) updateMediaPaddings() updateMediaPaddings() updatePageIndicator() updatePageIndicator() } } Loading
packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt +34 −22 Original line number Original line Diff line number Diff line Loading @@ -27,8 +27,10 @@ import androidx.annotation.AnyThread import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.concurrency.RepeatableExecutor import java.util.concurrent.Executor import javax.inject.Inject private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L Loading Loading @@ -65,7 +67,7 @@ private fun PlaybackState.computePosition(duration: Long): Long { } } /** ViewModel for seek bar in QS media player. */ /** ViewModel for seek bar in QS media player. */ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: RepeatableExecutor) { private var _data = Progress(false, false, null, null) private var _data = Progress(false, false, null, null) set(value) { set(value) { Loading @@ -89,10 +91,10 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { private var callback = object : MediaController.Callback() { private var callback = object : MediaController.Callback() { override fun onPlaybackStateChanged(state: PlaybackState) { override fun onPlaybackStateChanged(state: PlaybackState) { playbackState = state playbackState = state if (shouldPollPlaybackPosition()) { if (PlaybackState.STATE_NONE.equals(playbackState)) { checkPlaybackPosition() } else if (PlaybackState.STATE_NONE.equals(playbackState)) { clearController() clearController() } else { checkIfPollingNeeded() } } } } Loading @@ -100,12 +102,14 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { clearController() clearController() } } } } private var cancel: Runnable? = null /** Listening state (QS open or closed) is used to control polling of progress. */ /** Listening state (QS open or closed) is used to control polling of progress. */ var listening = true var listening = true set(value) { set(value) = bgExecutor.execute { if (value) { if (field != value) { checkPlaybackPosition() field = value checkIfPollingNeeded() } } } } Loading Loading @@ -137,9 +141,7 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { playbackState?.getState() == PlaybackState.STATE_NONE || playbackState?.getState() == PlaybackState.STATE_NONE || (duration != null && duration <= 0)) false else true (duration != null && duration <= 0)) false else true _data = Progress(enabled, seekAvailable, position, duration) _data = Progress(enabled, seekAvailable, position, duration) if (shouldPollPlaybackPosition()) { checkIfPollingNeeded() checkPlaybackPosition() } } } /** /** Loading @@ -151,6 +153,8 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { fun clearController() = bgExecutor.execute { fun clearController() = bgExecutor.execute { controller = null controller = null playbackState = null playbackState = null cancel?.run() cancel = null _data = _data.copy(enabled = false) _data = _data.copy(enabled = false) } } Loading @@ -158,26 +162,34 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { * Call to clean up any resources. * Call to clean up any resources. */ */ @AnyThread @AnyThread fun onDestroy() { fun onDestroy() = bgExecutor.execute { controller = null controller = null playbackState = null playbackState = null cancel?.run() cancel = null } } @AnyThread @WorkerThread private fun checkPlaybackPosition(): Runnable = bgExecutor.executeDelayed({ private fun checkPlaybackPosition() { val duration = _data.duration ?: -1 val duration = _data.duration ?: -1 val currentPosition = playbackState?.computePosition(duration.toLong())?.toInt() val currentPosition = playbackState?.computePosition(duration.toLong())?.toInt() if (currentPosition != null && _data.elapsedTime != currentPosition) { if (currentPosition != null && _data.elapsedTime != currentPosition) { _data = _data.copy(elapsedTime = currentPosition) _data = _data.copy(elapsedTime = currentPosition) } } if (shouldPollPlaybackPosition()) { checkPlaybackPosition() } } }, POSITION_UPDATE_INTERVAL_MILLIS) @WorkerThread @WorkerThread private fun shouldPollPlaybackPosition(): Boolean { private fun checkIfPollingNeeded() { return listening && playbackState?.isInMotion() ?: false val needed = listening && playbackState?.isInMotion() ?: false if (needed) { if (cancel == null) { cancel = bgExecutor.executeRepeatedly(this::checkPlaybackPosition, 0L, POSITION_UPDATE_INTERVAL_MILLIS) } } else { cancel?.run() cancel = null } } } /** Gets a listener to attach to the seek bar to handle seeking. */ /** Gets a listener to attach to the seek bar to handle seeking. */ Loading @@ -194,7 +206,7 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { private class SeekBarChangeListener( private class SeekBarChangeListener( val viewModel: SeekBarViewModel, val viewModel: SeekBarViewModel, val bgExecutor: DelayableExecutor val bgExecutor: Executor ) : SeekBar.OnSeekBarChangeListener { ) : SeekBar.OnSeekBarChangeListener { override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) { override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) { if (fromUser) { if (fromUser) { Loading
packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +10 −6 Original line number Original line Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.widget.ImageButton import android.widget.ImageView import android.widget.ImageView import android.widget.SeekBar import android.widget.SeekBar import android.widget.TextView import android.widget.TextView import androidx.lifecycle.LiveData import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase Loading @@ -41,6 +42,7 @@ import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.After import org.junit.Before import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.Test import org.junit.runner.RunWith import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentCaptor Loading @@ -48,6 +50,7 @@ import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit private const val KEY = "TEST_KEY" private const val KEY = "TEST_KEY" private const val APP = "APP" private const val APP = "APP" Loading @@ -73,6 +76,8 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var holder: PlayerViewHolder @Mock private lateinit var holder: PlayerViewHolder @Mock private lateinit var view: TransitionLayout @Mock private lateinit var view: TransitionLayout @Mock private lateinit var mediaHostStatesManager: MediaHostStatesManager @Mock private lateinit var mediaHostStatesManager: MediaHostStatesManager @Mock private lateinit var seekBarViewModel: SeekBarViewModel @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress> private lateinit var appIcon: ImageView private lateinit var appIcon: ImageView private lateinit var appName: TextView private lateinit var appName: TextView private lateinit var albumView: ImageView private lateinit var albumView: ImageView Loading @@ -94,18 +99,17 @@ public class MediaControlPanelTest : SysuiTestCase() { private val device = MediaDeviceData(true, null, DEVICE_NAME) private val device = MediaDeviceData(true, null, DEVICE_NAME) private val disabledDevice = MediaDeviceData(false, null, null) private val disabledDevice = MediaDeviceData(false, null, null) @JvmField @Rule val mockito = MockitoJUnit.rule() @Before @Before fun setUp() { fun setUp() { bgExecutor = FakeExecutor(FakeSystemClock()) bgExecutor = FakeExecutor(FakeSystemClock()) activityStarter = mock(ActivityStarter::class.java) player = MediaControlPanel(context, bgExecutor, activityStarter, mediaHostStatesManager, mediaHostStatesManager = mock(MediaHostStatesManager::class.java) seekBarViewModel) whenever(seekBarViewModel.progress).thenReturn(seekBarData) player = MediaControlPanel(context, bgExecutor, activityStarter, mediaHostStatesManager) // Mock out a view holder for the player to attach to. // Mock out a view holder for the player to attach to. holder = mock(PlayerViewHolder::class.java) view = mock(TransitionLayout::class.java) whenever(holder.player).thenReturn(view) whenever(holder.player).thenReturn(view) appIcon = ImageView(context) appIcon = ImageView(context) whenever(holder.appIcon).thenReturn(appIcon) whenever(holder.appIcon).thenReturn(appIcon) Loading
packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt +2 −1 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeRepeatableExecutor import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat Loading Loading @@ -71,7 +72,7 @@ public class SeekBarViewModelTest : SysuiTestCase() { @Before @Before fun setUp() { fun setUp() { fakeExecutor = FakeExecutor(FakeSystemClock()) fakeExecutor = FakeExecutor(FakeSystemClock()) viewModel = SeekBarViewModel(fakeExecutor) viewModel = SeekBarViewModel(FakeRepeatableExecutor(fakeExecutor)) mockController = mock(MediaController::class.java) mockController = mock(MediaController::class.java) whenever(mockController.sessionToken).thenReturn(token1) whenever(mockController.sessionToken).thenReturn(token1) mockTransport = mock(MediaController.TransportControls::class.java) mockTransport = mock(MediaController.TransportControls::class.java) Loading