diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 1691c53386d68..aee7a4649d088 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -228,7 +228,7 @@ public class MediaControlPanel { mLayoutAnimationHelper = new LayoutAnimationHelper(motionView); GoneChildrenHideHelper.clipGoneChildrenOnLayout(motionView); mKeyFrames = motionView.getDefinedTransitions().get(0).getKeyFrameList(); - mSeekBarObserver = new SeekBarObserver(motionView); + mSeekBarObserver = new SeekBarObserver(vh); mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver); SeekBar bar = vh.getSeekBar(); bar.setOnSeekBarChangeListener(mSeekBarViewModel.getSeekBarListener()); diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt index 110b08c4b8085..cd8ed265bd532 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt @@ -16,58 +16,41 @@ package com.android.systemui.media -import android.content.res.ColorStateList -import android.graphics.Color import android.text.format.DateUtils -import android.view.View -import android.widget.SeekBar -import android.widget.TextView import androidx.annotation.UiThread import androidx.lifecycle.Observer -import com.android.systemui.R - /** * Observer for changes from SeekBarViewModel. * *

Updates the seek bar views in response to changes to the model. */ -class SeekBarObserver(view: View) : Observer { - - private val seekBarView: SeekBar - private val elapsedTimeView: TextView - private val totalTimeView: TextView - - init { - seekBarView = view.findViewById(R.id.media_progress_bar) - elapsedTimeView = view.findViewById(R.id.media_elapsed_time) - totalTimeView = view.findViewById(R.id.media_total_time) - } +class SeekBarObserver(private val holder: PlayerViewHolder) : Observer { /** Updates seek bar views when the data model changes. */ @UiThread override fun onChanged(data: SeekBarViewModel.Progress) { if (!data.enabled) { - seekBarView.setEnabled(false) - seekBarView.getThumb().setAlpha(0) - seekBarView.setProgress(0) - elapsedTimeView.setText("") - totalTimeView.setText("") + holder.seekBar.setEnabled(false) + holder.seekBar.getThumb().setAlpha(0) + holder.seekBar.setProgress(0) + holder.elapsedTimeView.setText("") + holder.totalTimeView.setText("") return } - seekBarView.getThumb().setAlpha(if (data.seekAvailable) 255 else 0) - seekBarView.setEnabled(data.seekAvailable) + holder.seekBar.getThumb().setAlpha(if (data.seekAvailable) 255 else 0) + holder.seekBar.setEnabled(data.seekAvailable) data.elapsedTime?.let { - seekBarView.setProgress(it) - elapsedTimeView.setText(DateUtils.formatElapsedTime( + holder.seekBar.setProgress(it) + holder.elapsedTimeView.setText(DateUtils.formatElapsedTime( it / DateUtils.SECOND_IN_MILLIS)) } data.duration?.let { - seekBarView.setMax(it) - totalTimeView.setText(DateUtils.formatElapsedTime( + holder.seekBar.setMax(it) + holder.totalTimeView.setText(DateUtils.formatElapsedTime( it / DateUtils.SECOND_IN_MILLIS)) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt new file mode 100644 index 0000000000000..60d15a8fb480b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import android.content.res.ColorStateList +import android.graphics.Color +import android.media.MediaMetadata +import android.media.session.MediaSession +import android.media.session.PlaybackState +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.SeekBar +import android.widget.TextView + +import androidx.constraintlayout.motion.widget.MotionLayout +import androidx.constraintlayout.motion.widget.MotionScene +import androidx.constraintlayout.widget.ConstraintSet +import androidx.test.filters.SmallTest + +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock + +import com.google.common.truth.Truth.assertThat + +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` as whenever + +import java.util.ArrayList + +private const val KEY = "TEST_KEY" +private const val APP = "APP" +private const val BG_COLOR = Color.RED +private const val PACKAGE = "PKG" +private const val ARTIST = "ARTIST" +private const val TITLE = "TITLE" +private const val DEVICE_NAME = "DEVICE_NAME" +private const val SESSION_KEY = "SESSION_KEY" +private const val SESSION_ARTIST = "SESSION_ARTIST" +private const val SESSION_TITLE = "SESSION_TITLE" + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class MediaControlPanelTest : SysuiTestCase() { + + private lateinit var player: MediaControlPanel + + private lateinit var fgExecutor: FakeExecutor + private lateinit var bgExecutor: FakeExecutor + @Mock private lateinit var activityStarter: ActivityStarter + + @Mock private lateinit var holder: PlayerViewHolder + @Mock private lateinit var motion: MotionLayout + private lateinit var background: TextView + private lateinit var appIcon: ImageView + private lateinit var appName: TextView + private lateinit var albumView: ImageView + private lateinit var titleText: TextView + private lateinit var artistText: TextView + private lateinit var seamless: ViewGroup + private lateinit var seamlessIcon: ImageView + private lateinit var seamlessText: TextView + private lateinit var seekBar: SeekBar + private lateinit var elapsedTimeView: TextView + private lateinit var totalTimeView: TextView + private lateinit var action0: ImageButton + private lateinit var action1: ImageButton + private lateinit var action2: ImageButton + private lateinit var action3: ImageButton + private lateinit var action4: ImageButton + + private lateinit var session: MediaSession + + @Before + fun setUp() { + fgExecutor = FakeExecutor(FakeSystemClock()) + bgExecutor = FakeExecutor(FakeSystemClock()) + + activityStarter = mock(ActivityStarter::class.java) + + player = MediaControlPanel(context, null, fgExecutor, bgExecutor, activityStarter) + + // Mock out a view holder for the player to attach to. + holder = mock(PlayerViewHolder::class.java) + motion = mock(MotionLayout::class.java) + val trans: ArrayList = ArrayList() + trans.add(mock(MotionScene.Transition::class.java)) + whenever(motion.definedTransitions).thenReturn(trans) + val constraintSet = mock(ConstraintSet::class.java) + whenever(motion.getConstraintSet(R.id.expanded)).thenReturn(constraintSet) + whenever(motion.getConstraintSet(R.id.collapsed)).thenReturn(constraintSet) + whenever(holder.player).thenReturn(motion) + background = TextView(context) + whenever(holder.background).thenReturn(background) + appIcon = ImageView(context) + whenever(holder.appIcon).thenReturn(appIcon) + appName = TextView(context) + whenever(holder.appName).thenReturn(appName) + albumView = ImageView(context) + whenever(holder.albumView).thenReturn(albumView) + titleText = TextView(context) + whenever(holder.titleText).thenReturn(titleText) + artistText = TextView(context) + whenever(holder.artistText).thenReturn(artistText) + seamless = FrameLayout(context) + whenever(holder.seamless).thenReturn(seamless) + seamlessIcon = ImageView(context) + whenever(holder.seamlessIcon).thenReturn(seamlessIcon) + seamlessText = TextView(context) + whenever(holder.seamlessText).thenReturn(seamlessText) + seekBar = SeekBar(context) + whenever(holder.seekBar).thenReturn(seekBar) + elapsedTimeView = TextView(context) + whenever(holder.elapsedTimeView).thenReturn(elapsedTimeView) + totalTimeView = TextView(context) + whenever(holder.totalTimeView).thenReturn(totalTimeView) + action0 = ImageButton(context) + whenever(holder.action0).thenReturn(action0) + action1 = ImageButton(context) + whenever(holder.action1).thenReturn(action1) + action2 = ImageButton(context) + whenever(holder.action2).thenReturn(action2) + action3 = ImageButton(context) + whenever(holder.action3).thenReturn(action3) + action4 = ImageButton(context) + whenever(holder.action4).thenReturn(action4) + + // Create media session + val metadataBuilder = MediaMetadata.Builder().apply { + putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST) + putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) + } + val playbackBuilder = PlaybackState.Builder().apply { + setState(PlaybackState.STATE_PAUSED, 6000L, 1f) + setActions(PlaybackState.ACTION_PLAY) + } + session = MediaSession(context, SESSION_KEY).apply { + setMetadata(metadataBuilder.build()) + setPlaybackState(playbackBuilder.build()) + } + session.setActive(true) + } + + @After + fun tearDown() { + session.release() + player.onDestroy() + } + + @Test + fun bindWhenUnattached() { + val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), + emptyList(), PACKAGE, null, null) + player.bind(state) + assertThat(player.isPlaying()).isFalse() + } + + @Test + fun bindText() { + player.attach(holder) + val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), + emptyList(), PACKAGE, session.getSessionToken(), null) + player.bind(state) + assertThat(appName.getText()).isEqualTo(APP) + assertThat(titleText.getText()).isEqualTo(TITLE) + assertThat(artistText.getText()).isEqualTo(ARTIST) + } + + @Test + fun bindBackgroundColor() { + player.attach(holder) + val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), + emptyList(), PACKAGE, session.getSessionToken(), null) + player.bind(state) + assertThat(background.getBackgroundTintList()).isEqualTo(ColorStateList.valueOf(BG_COLOR)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt index 58ee79e39279c..75018df023cc1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt @@ -22,7 +22,6 @@ import android.view.View import android.widget.SeekBar import android.widget.TextView import androidx.test.filters.SmallTest -import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -38,23 +37,21 @@ import org.mockito.Mockito.`when` as whenever public class SeekBarObserverTest : SysuiTestCase() { private lateinit var observer: SeekBarObserver - @Mock private lateinit var mockView: View + @Mock private lateinit var mockHolder: PlayerViewHolder private lateinit var seekBarView: SeekBar private lateinit var elapsedTimeView: TextView private lateinit var totalTimeView: TextView @Before fun setUp() { - mockView = mock(View::class.java) + mockHolder = mock(PlayerViewHolder::class.java) seekBarView = SeekBar(context) elapsedTimeView = TextView(context) totalTimeView = TextView(context) - whenever( - mockView.findViewById(R.id.media_progress_bar)).thenReturn(seekBarView) - whenever( - mockView.findViewById(R.id.media_elapsed_time)).thenReturn(elapsedTimeView) - whenever(mockView.findViewById(R.id.media_total_time)).thenReturn(totalTimeView) - observer = SeekBarObserver(mockView) + whenever(mockHolder.seekBar).thenReturn(seekBarView) + whenever(mockHolder.elapsedTimeView).thenReturn(elapsedTimeView) + whenever(mockHolder.totalTimeView).thenReturn(totalTimeView) + observer = SeekBarObserver(mockHolder) } @Test