Merge "Merge "Add test for MediaControlPanel" into rvc-dev am: 0606dd5670 am: c7d8ccce3e am: 7060b34560" into rvc-qpr-dev-plus-aosp am: 86f8a2e61f
Change-Id: I8905bea0c2a4693f6004e8f3270c957f72bc9110
This commit is contained in:
@@ -228,7 +228,7 @@ public class MediaControlPanel {
|
|||||||
mLayoutAnimationHelper = new LayoutAnimationHelper(motionView);
|
mLayoutAnimationHelper = new LayoutAnimationHelper(motionView);
|
||||||
GoneChildrenHideHelper.clipGoneChildrenOnLayout(motionView);
|
GoneChildrenHideHelper.clipGoneChildrenOnLayout(motionView);
|
||||||
mKeyFrames = motionView.getDefinedTransitions().get(0).getKeyFrameList();
|
mKeyFrames = motionView.getDefinedTransitions().get(0).getKeyFrameList();
|
||||||
mSeekBarObserver = new SeekBarObserver(motionView);
|
mSeekBarObserver = new SeekBarObserver(vh);
|
||||||
mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver);
|
mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver);
|
||||||
SeekBar bar = vh.getSeekBar();
|
SeekBar bar = vh.getSeekBar();
|
||||||
bar.setOnSeekBarChangeListener(mSeekBarViewModel.getSeekBarListener());
|
bar.setOnSeekBarChangeListener(mSeekBarViewModel.getSeekBarListener());
|
||||||
|
|||||||
@@ -16,58 +16,41 @@
|
|||||||
|
|
||||||
package com.android.systemui.media
|
package com.android.systemui.media
|
||||||
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.view.View
|
|
||||||
import android.widget.SeekBar
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.annotation.UiThread
|
import androidx.annotation.UiThread
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
|
||||||
import com.android.systemui.R
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Observer for changes from SeekBarViewModel.
|
* Observer for changes from SeekBarViewModel.
|
||||||
*
|
*
|
||||||
* <p>Updates the seek bar views in response to changes to the model.
|
* <p>Updates the seek bar views in response to changes to the model.
|
||||||
*/
|
*/
|
||||||
class SeekBarObserver(view: View) : Observer<SeekBarViewModel.Progress> {
|
class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarViewModel.Progress> {
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Updates seek bar views when the data model changes. */
|
/** Updates seek bar views when the data model changes. */
|
||||||
@UiThread
|
@UiThread
|
||||||
override fun onChanged(data: SeekBarViewModel.Progress) {
|
override fun onChanged(data: SeekBarViewModel.Progress) {
|
||||||
if (!data.enabled) {
|
if (!data.enabled) {
|
||||||
seekBarView.setEnabled(false)
|
holder.seekBar.setEnabled(false)
|
||||||
seekBarView.getThumb().setAlpha(0)
|
holder.seekBar.getThumb().setAlpha(0)
|
||||||
seekBarView.setProgress(0)
|
holder.seekBar.setProgress(0)
|
||||||
elapsedTimeView.setText("")
|
holder.elapsedTimeView.setText("")
|
||||||
totalTimeView.setText("")
|
holder.totalTimeView.setText("")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
seekBarView.getThumb().setAlpha(if (data.seekAvailable) 255 else 0)
|
holder.seekBar.getThumb().setAlpha(if (data.seekAvailable) 255 else 0)
|
||||||
seekBarView.setEnabled(data.seekAvailable)
|
holder.seekBar.setEnabled(data.seekAvailable)
|
||||||
|
|
||||||
data.elapsedTime?.let {
|
data.elapsedTime?.let {
|
||||||
seekBarView.setProgress(it)
|
holder.seekBar.setProgress(it)
|
||||||
elapsedTimeView.setText(DateUtils.formatElapsedTime(
|
holder.elapsedTimeView.setText(DateUtils.formatElapsedTime(
|
||||||
it / DateUtils.SECOND_IN_MILLIS))
|
it / DateUtils.SECOND_IN_MILLIS))
|
||||||
}
|
}
|
||||||
|
|
||||||
data.duration?.let {
|
data.duration?.let {
|
||||||
seekBarView.setMax(it)
|
holder.seekBar.setMax(it)
|
||||||
totalTimeView.setText(DateUtils.formatElapsedTime(
|
holder.totalTimeView.setText(DateUtils.formatElapsedTime(
|
||||||
it / DateUtils.SECOND_IN_MILLIS))
|
it / DateUtils.SECOND_IN_MILLIS))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<MotionScene.Transition> = 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,6 @@ import android.view.View
|
|||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.R
|
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@@ -38,23 +37,21 @@ import org.mockito.Mockito.`when` as whenever
|
|||||||
public class SeekBarObserverTest : SysuiTestCase() {
|
public class SeekBarObserverTest : SysuiTestCase() {
|
||||||
|
|
||||||
private lateinit var observer: SeekBarObserver
|
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 seekBarView: SeekBar
|
||||||
private lateinit var elapsedTimeView: TextView
|
private lateinit var elapsedTimeView: TextView
|
||||||
private lateinit var totalTimeView: TextView
|
private lateinit var totalTimeView: TextView
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
mockView = mock(View::class.java)
|
mockHolder = mock(PlayerViewHolder::class.java)
|
||||||
seekBarView = SeekBar(context)
|
seekBarView = SeekBar(context)
|
||||||
elapsedTimeView = TextView(context)
|
elapsedTimeView = TextView(context)
|
||||||
totalTimeView = TextView(context)
|
totalTimeView = TextView(context)
|
||||||
whenever<SeekBar>(
|
whenever(mockHolder.seekBar).thenReturn(seekBarView)
|
||||||
mockView.findViewById(R.id.media_progress_bar)).thenReturn(seekBarView)
|
whenever(mockHolder.elapsedTimeView).thenReturn(elapsedTimeView)
|
||||||
whenever<TextView>(
|
whenever(mockHolder.totalTimeView).thenReturn(totalTimeView)
|
||||||
mockView.findViewById(R.id.media_elapsed_time)).thenReturn(elapsedTimeView)
|
observer = SeekBarObserver(mockHolder)
|
||||||
whenever<TextView>(mockView.findViewById(R.id.media_total_time)).thenReturn(totalTimeView)
|
|
||||||
observer = SeekBarObserver(mockView)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user