Implement seekbar animation
Test: manual Test: atest SeekBarObserverTest Test: atest SquigglyProgressTest Bug: 220873625 Change-Id: I161fe0eeb30bde425c1fbad5d5fdad924e628f2d
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<com.android.systemui.media.SquigglyProgress />
|
||||
@@ -979,6 +979,10 @@
|
||||
<dimen name="qs_media_session_disabled_seekbar_vertical_padding">16dp</dimen>
|
||||
<dimen name="qs_media_session_height_expanded">184dp</dimen>
|
||||
<dimen name="qs_media_session_height_collapsed">128dp</dimen>
|
||||
<dimen name="qs_media_seekbar_progress_wavelength">20dp</dimen>
|
||||
<dimen name="qs_media_seekbar_progress_amplitude">1.5dp</dimen>
|
||||
<dimen name="qs_media_seekbar_progress_phase">8dp</dimen>
|
||||
<dimen name="qs_media_seekbar_progress_stroke_width">2dp</dimen>
|
||||
|
||||
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
|
||||
<dimen name="qs_aa_media_rec_album_size_collapsed">72dp</dimen>
|
||||
|
||||
@@ -590,6 +590,7 @@
|
||||
|
||||
<style name="MediaPlayer.ProgressBar" parent="@android:style/Widget.ProgressBar.Horizontal">
|
||||
<item name="android:thumbTint">?android:attr/textColorPrimary</item>
|
||||
<item name="android:progressDrawable">@drawable/media_squiggly_progress</item>
|
||||
<item name="android:progressTint">?android:attr/textColorPrimary</item>
|
||||
<item name="android:progressBackgroundTint">?android:attr/textColorTertiary</item>
|
||||
<item name="android:clickable">true</item>
|
||||
|
||||
@@ -50,25 +50,46 @@ class SeekBarObserver(
|
||||
.getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_vertical_padding)
|
||||
}
|
||||
|
||||
init {
|
||||
val seekBarProgressWavelength = holder.seekBar.context.resources
|
||||
.getDimensionPixelSize(R.dimen.qs_media_seekbar_progress_wavelength).toFloat()
|
||||
val seekBarProgressAmplitude = holder.seekBar.context.resources
|
||||
.getDimensionPixelSize(R.dimen.qs_media_seekbar_progress_amplitude).toFloat()
|
||||
val seekBarProgressPhase = holder.seekBar.context.resources
|
||||
.getDimensionPixelSize(R.dimen.qs_media_seekbar_progress_phase).toFloat()
|
||||
val seekBarProgressStrokeWidth = holder.seekBar.context.resources
|
||||
.getDimensionPixelSize(R.dimen.qs_media_seekbar_progress_stroke_width).toFloat()
|
||||
val progressDrawable = holder.seekBar.progressDrawable as? SquigglyProgress
|
||||
progressDrawable?.let {
|
||||
it.waveLength = seekBarProgressWavelength
|
||||
it.lineAmplitude = seekBarProgressAmplitude
|
||||
it.phaseSpeed = seekBarProgressPhase
|
||||
it.strokeWidth = seekBarProgressStrokeWidth
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates seek bar views when the data model changes. */
|
||||
@UiThread
|
||||
override fun onChanged(data: SeekBarViewModel.Progress) {
|
||||
val progressDrawable = holder.seekBar.progressDrawable as? SquigglyProgress
|
||||
if (!data.enabled) {
|
||||
if (holder.seekBar.maxHeight != seekBarDisabledHeight) {
|
||||
holder.seekBar.maxHeight = seekBarDisabledHeight
|
||||
setVerticalPadding(seekBarDisabledVerticalPadding)
|
||||
}
|
||||
holder.seekBar.setEnabled(false)
|
||||
holder.seekBar.getThumb().setAlpha(0)
|
||||
holder.seekBar.setProgress(0)
|
||||
holder.elapsedTimeView?.setText("")
|
||||
holder.totalTimeView?.setText("")
|
||||
holder.seekBar.isEnabled = false
|
||||
progressDrawable?.animate = false
|
||||
holder.seekBar.thumb.alpha = 0
|
||||
holder.seekBar.progress = 0
|
||||
holder.elapsedTimeView?.text = ""
|
||||
holder.totalTimeView?.text = ""
|
||||
holder.seekBar.contentDescription = ""
|
||||
return
|
||||
}
|
||||
|
||||
holder.seekBar.getThumb().setAlpha(if (data.seekAvailable) 255 else 0)
|
||||
holder.seekBar.setEnabled(data.seekAvailable)
|
||||
holder.seekBar.thumb.alpha = if (data.seekAvailable) 255 else 0
|
||||
holder.seekBar.isEnabled = data.seekAvailable
|
||||
progressDrawable?.animate = data.playing
|
||||
|
||||
if (holder.seekBar.maxHeight != seekBarEnabledMaxHeight) {
|
||||
holder.seekBar.maxHeight = seekBarEnabledMaxHeight
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.core.view.GestureDetectorCompat
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.statusbar.NotificationMediaManager
|
||||
import com.android.systemui.util.concurrency.RepeatableExecutor
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -73,7 +74,7 @@ private fun PlaybackState.computePosition(duration: Long): Long {
|
||||
class SeekBarViewModel @Inject constructor(
|
||||
@Background private val bgExecutor: RepeatableExecutor
|
||||
) {
|
||||
private var _data = Progress(false, false, null, 0)
|
||||
private var _data = Progress(false, false, false, null, 0)
|
||||
set(value) {
|
||||
field = value
|
||||
_progress.postValue(value)
|
||||
@@ -192,10 +193,12 @@ class SeekBarViewModel @Inject constructor(
|
||||
val seekAvailable = ((playbackState?.actions ?: 0L) and PlaybackState.ACTION_SEEK_TO) != 0L
|
||||
val position = playbackState?.position?.toInt()
|
||||
val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
|
||||
val playing = NotificationMediaManager
|
||||
.isPlayingState(playbackState?.state ?: PlaybackState.STATE_NONE)
|
||||
val enabled = if (playbackState == null ||
|
||||
playbackState?.getState() == PlaybackState.STATE_NONE ||
|
||||
(duration <= 0)) false else true
|
||||
_data = Progress(enabled, seekAvailable, position, duration)
|
||||
_data = Progress(enabled, seekAvailable, playing, position, duration)
|
||||
checkIfPollingNeeded()
|
||||
}
|
||||
|
||||
@@ -412,6 +415,7 @@ class SeekBarViewModel @Inject constructor(
|
||||
data class Progress(
|
||||
val enabled: Boolean,
|
||||
val seekAvailable: Boolean,
|
||||
val playing: Boolean,
|
||||
val elapsedTime: Int?,
|
||||
val duration: Int
|
||||
)
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
package com.android.systemui.media
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Path
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.SystemClock
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.systemui.animation.Interpolators
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
|
||||
private const val TAG = "Squiggly"
|
||||
|
||||
private const val TWO_PI = (Math.PI * 2f).toFloat()
|
||||
@VisibleForTesting
|
||||
internal const val DISABLED_ALPHA = 77
|
||||
|
||||
class SquigglyProgress : Drawable() {
|
||||
|
||||
private val wavePaint = Paint()
|
||||
private val linePaint = Paint()
|
||||
private val path = Path()
|
||||
private var heightFraction = 0f
|
||||
private var heightAnimator: ValueAnimator? = null
|
||||
private var phaseOffset = 0f
|
||||
private var lastFrameTime = -1L
|
||||
|
||||
// Horizontal length of the sine wave
|
||||
var waveLength = 0f
|
||||
// Height of each peak of the sine wave
|
||||
var lineAmplitude = 0f
|
||||
// Line speed in px per second
|
||||
var phaseSpeed = 0f
|
||||
// Progress stroke width, both for wave and solid line
|
||||
var strokeWidth = 0f
|
||||
set(value) {
|
||||
if (field == value) {
|
||||
return
|
||||
}
|
||||
field = value
|
||||
wavePaint.strokeWidth = value
|
||||
linePaint.strokeWidth = value
|
||||
}
|
||||
|
||||
init {
|
||||
wavePaint.strokeCap = Paint.Cap.ROUND
|
||||
linePaint.strokeCap = Paint.Cap.ROUND
|
||||
linePaint.style = Paint.Style.STROKE
|
||||
wavePaint.style = Paint.Style.STROKE
|
||||
linePaint.alpha = DISABLED_ALPHA
|
||||
}
|
||||
|
||||
var animate: Boolean = false
|
||||
set(value) {
|
||||
if (field == value) {
|
||||
return
|
||||
}
|
||||
field = value
|
||||
if (field) {
|
||||
lastFrameTime = SystemClock.uptimeMillis()
|
||||
}
|
||||
heightAnimator?.cancel()
|
||||
heightAnimator = ValueAnimator.ofFloat(heightFraction, if (animate) 1f else 0f).apply {
|
||||
if (animate) {
|
||||
startDelay = 60
|
||||
duration = 800
|
||||
interpolator = Interpolators.EMPHASIZED_DECELERATE
|
||||
} else {
|
||||
duration = 550
|
||||
interpolator = Interpolators.STANDARD_DECELERATE
|
||||
}
|
||||
addUpdateListener {
|
||||
heightFraction = it.animatedValue as Float
|
||||
invalidateSelf()
|
||||
}
|
||||
addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
heightAnimator = null
|
||||
}
|
||||
})
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
if (animate) {
|
||||
invalidateSelf()
|
||||
val now = SystemClock.uptimeMillis()
|
||||
phaseOffset -= (now - lastFrameTime) / 1000f * phaseSpeed
|
||||
phaseOffset %= waveLength
|
||||
lastFrameTime = now
|
||||
}
|
||||
|
||||
val totalProgressPx = (bounds.width() * (level / 10_000f))
|
||||
canvas.save()
|
||||
canvas.translate(bounds.left.toFloat(), bounds.centerY().toFloat())
|
||||
// Clip drawing, so we stop at the thumb
|
||||
canvas.clipRect(
|
||||
0f,
|
||||
-lineAmplitude - strokeWidth,
|
||||
totalProgressPx,
|
||||
lineAmplitude + strokeWidth)
|
||||
|
||||
// The squiggly line
|
||||
val start = phaseOffset
|
||||
var currentX = start
|
||||
var waveSign = 1f
|
||||
path.rewind()
|
||||
path.moveTo(start, lineAmplitude * heightFraction)
|
||||
while (currentX < totalProgressPx) {
|
||||
val nextX = currentX + waveLength / 2f
|
||||
val nextWaveSign = waveSign * -1
|
||||
path.cubicTo(
|
||||
currentX + waveLength / 4f, lineAmplitude * waveSign * heightFraction,
|
||||
nextX - waveLength / 4f, lineAmplitude * nextWaveSign * heightFraction,
|
||||
nextX, lineAmplitude * nextWaveSign * heightFraction)
|
||||
currentX = nextX
|
||||
waveSign = nextWaveSign
|
||||
}
|
||||
wavePaint.style = Paint.Style.STROKE
|
||||
canvas.drawPath(path, wavePaint)
|
||||
canvas.restore()
|
||||
|
||||
// Draw round line cap at the beginning of the wave
|
||||
val startAmp = cos(abs(phaseOffset) / waveLength * TWO_PI)
|
||||
val p = Paint()
|
||||
p.color = Color.WHITE
|
||||
canvas.drawPoint(
|
||||
bounds.left.toFloat(),
|
||||
bounds.centerY() + startAmp * lineAmplitude * heightFraction,
|
||||
wavePaint)
|
||||
|
||||
// Draw continuous line, to the right of the thumb
|
||||
canvas.drawLine(
|
||||
bounds.left.toFloat() + totalProgressPx,
|
||||
bounds.centerY().toFloat(),
|
||||
bounds.width().toFloat(),
|
||||
bounds.centerY().toFloat(),
|
||||
linePaint)
|
||||
}
|
||||
|
||||
override fun getOpacity(): Int {
|
||||
return PixelFormat.TRANSLUCENT
|
||||
}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {
|
||||
wavePaint.colorFilter = colorFilter
|
||||
linePaint.colorFilter = colorFilter
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
wavePaint.alpha = alpha
|
||||
linePaint.alpha = (DISABLED_ALPHA * (alpha / 255f)).toInt()
|
||||
}
|
||||
|
||||
override fun getAlpha(): Int {
|
||||
return wavePaint.alpha
|
||||
}
|
||||
|
||||
override fun setTint(tintColor: Int) {
|
||||
wavePaint.color = tintColor
|
||||
linePaint.color = tintColor
|
||||
}
|
||||
|
||||
override fun onLevelChange(level: Int): Boolean {
|
||||
return animate
|
||||
}
|
||||
|
||||
override fun setTintList(tint: ColorStateList?) {
|
||||
if (tint == null) {
|
||||
return
|
||||
}
|
||||
wavePaint.color = tint.defaultColor
|
||||
linePaint.color = tint.defaultColor
|
||||
}
|
||||
}
|
||||
@@ -26,29 +26,33 @@ import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class SeekBarObserverTest : SysuiTestCase() {
|
||||
class SeekBarObserverTest : SysuiTestCase() {
|
||||
|
||||
private val disabledHeight = 1
|
||||
private val enabledHeight = 2
|
||||
|
||||
private lateinit var observer: SeekBarObserver
|
||||
@Mock private lateinit var mockHolder: PlayerViewHolder
|
||||
@Mock private lateinit var mockSquigglyProgress: SquigglyProgress
|
||||
private lateinit var seekBarView: SeekBar
|
||||
private lateinit var elapsedTimeView: TextView
|
||||
private lateinit var totalTimeView: TextView
|
||||
|
||||
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockHolder = mock(PlayerViewHolder::class.java)
|
||||
|
||||
context.orCreateTestableResources
|
||||
.addOverride(R.dimen.qs_media_enabled_seekbar_height, enabledHeight)
|
||||
@@ -56,6 +60,7 @@ public class SeekBarObserverTest : SysuiTestCase() {
|
||||
.addOverride(R.dimen.qs_media_disabled_seekbar_height, disabledHeight)
|
||||
|
||||
seekBarView = SeekBar(context)
|
||||
seekBarView.progressDrawable = mockSquigglyProgress
|
||||
elapsedTimeView = TextView(context)
|
||||
totalTimeView = TextView(context)
|
||||
whenever(mockHolder.seekBar).thenReturn(seekBarView)
|
||||
@@ -69,7 +74,7 @@ public class SeekBarObserverTest : SysuiTestCase() {
|
||||
fun seekBarGone() {
|
||||
// WHEN seek bar is disabled
|
||||
val isEnabled = false
|
||||
val data = SeekBarViewModel.Progress(isEnabled, false, null, 0)
|
||||
val data = SeekBarViewModel.Progress(isEnabled, false, false, null, 0)
|
||||
observer.onChanged(data)
|
||||
// THEN seek bar shows just a thin line with no text
|
||||
assertThat(seekBarView.isEnabled()).isFalse()
|
||||
@@ -84,7 +89,7 @@ public class SeekBarObserverTest : SysuiTestCase() {
|
||||
fun seekBarVisible() {
|
||||
// WHEN seek bar is enabled
|
||||
val isEnabled = true
|
||||
val data = SeekBarViewModel.Progress(isEnabled, true, 3000, 12000)
|
||||
val data = SeekBarViewModel.Progress(isEnabled, true, false, 3000, 12000)
|
||||
observer.onChanged(data)
|
||||
// THEN seek bar is visible and thick
|
||||
assertThat(seekBarView.getVisibility()).isEqualTo(View.VISIBLE)
|
||||
@@ -96,7 +101,7 @@ public class SeekBarObserverTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun seekBarProgress() {
|
||||
// WHEN part of the track has been played
|
||||
val data = SeekBarViewModel.Progress(true, true, 3000, 120000)
|
||||
val data = SeekBarViewModel.Progress(true, true, true, 3000, 120000)
|
||||
observer.onChanged(data)
|
||||
// THEN seek bar shows the progress
|
||||
assertThat(seekBarView.progress).isEqualTo(3000)
|
||||
@@ -112,7 +117,7 @@ public class SeekBarObserverTest : SysuiTestCase() {
|
||||
fun seekBarDisabledWhenSeekNotAvailable() {
|
||||
// WHEN seek is not available
|
||||
val isSeekAvailable = false
|
||||
val data = SeekBarViewModel.Progress(true, isSeekAvailable, 3000, 120000)
|
||||
val data = SeekBarViewModel.Progress(true, isSeekAvailable, false, 3000, 120000)
|
||||
observer.onChanged(data)
|
||||
// THEN seek bar is not enabled
|
||||
assertThat(seekBarView.isEnabled()).isFalse()
|
||||
@@ -122,9 +127,29 @@ public class SeekBarObserverTest : SysuiTestCase() {
|
||||
fun seekBarEnabledWhenSeekNotAvailable() {
|
||||
// WHEN seek is available
|
||||
val isSeekAvailable = true
|
||||
val data = SeekBarViewModel.Progress(true, isSeekAvailable, 3000, 120000)
|
||||
val data = SeekBarViewModel.Progress(true, isSeekAvailable, false, 3000, 120000)
|
||||
observer.onChanged(data)
|
||||
// THEN seek bar is not enabled
|
||||
assertThat(seekBarView.isEnabled()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun seekBarPlaying() {
|
||||
// WHEN playing
|
||||
val isPlaying = true
|
||||
val data = SeekBarViewModel.Progress(true, true, isPlaying, 3000, 120000)
|
||||
observer.onChanged(data)
|
||||
// THEN progress drawable is animating
|
||||
verify(mockSquigglyProgress).animate = true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun seekBarNotPlaying() {
|
||||
// WHEN not playing
|
||||
val isPlaying = false
|
||||
val data = SeekBarViewModel.Progress(true, true, isPlaying, 3000, 120000)
|
||||
observer.onChanged(data)
|
||||
// THEN progress drawable is not animating
|
||||
verify(mockSquigglyProgress).animate = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.android.systemui.media
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.LightingColorFilter
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.anyFloat
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class SquigglyProgressTest : SysuiTestCase() {
|
||||
|
||||
private val colorFilter = LightingColorFilter(Color.RED, Color.BLUE)
|
||||
private val strokeWidth = 5f
|
||||
private val alpha = 128
|
||||
private val tint = Color.GREEN
|
||||
|
||||
lateinit var squigglyProgress: SquigglyProgress
|
||||
@Mock lateinit var canvas: Canvas
|
||||
@Captor lateinit var wavePaintCaptor: ArgumentCaptor<Paint>
|
||||
@Captor lateinit var linePaintCaptor: ArgumentCaptor<Paint>
|
||||
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
squigglyProgress = SquigglyProgress()
|
||||
squigglyProgress.waveLength = 30f
|
||||
squigglyProgress.lineAmplitude = 10f
|
||||
squigglyProgress.phaseSpeed = 8f
|
||||
squigglyProgress.strokeWidth = strokeWidth
|
||||
squigglyProgress.bounds = Rect(0, 0, 300, 30)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDrawPathAndLine() {
|
||||
squigglyProgress.draw(canvas)
|
||||
|
||||
verify(canvas).drawPath(any(), wavePaintCaptor.capture())
|
||||
verify(canvas).drawLine(anyFloat(), anyFloat(), anyFloat(), anyFloat(),
|
||||
linePaintCaptor.capture())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnLevelChanged() {
|
||||
assertThat(squigglyProgress.setLevel(5)).isFalse()
|
||||
squigglyProgress.animate = true
|
||||
assertThat(squigglyProgress.setLevel(4)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStrokeWidth() {
|
||||
squigglyProgress.draw(canvas)
|
||||
|
||||
verify(canvas).drawPath(any(), wavePaintCaptor.capture())
|
||||
verify(canvas).drawLine(anyFloat(), anyFloat(), anyFloat(), anyFloat(),
|
||||
linePaintCaptor.capture())
|
||||
|
||||
assertThat(wavePaintCaptor.value.strokeWidth).isEqualTo(strokeWidth)
|
||||
assertThat(linePaintCaptor.value.strokeWidth).isEqualTo(strokeWidth)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAlpha() {
|
||||
squigglyProgress.alpha = alpha
|
||||
squigglyProgress.draw(canvas)
|
||||
|
||||
verify(canvas).drawPath(any(), wavePaintCaptor.capture())
|
||||
verify(canvas).drawLine(anyFloat(), anyFloat(), anyFloat(), anyFloat(),
|
||||
linePaintCaptor.capture())
|
||||
|
||||
assertThat(squigglyProgress.alpha).isEqualTo(alpha)
|
||||
assertThat(wavePaintCaptor.value.alpha).isEqualTo(alpha)
|
||||
assertThat(linePaintCaptor.value.alpha).isEqualTo((alpha / 255f * DISABLED_ALPHA).toInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testColorFilter() {
|
||||
squigglyProgress.colorFilter = colorFilter
|
||||
squigglyProgress.draw(canvas)
|
||||
|
||||
verify(canvas).drawPath(any(), wavePaintCaptor.capture())
|
||||
verify(canvas).drawLine(anyFloat(), anyFloat(), anyFloat(), anyFloat(),
|
||||
linePaintCaptor.capture())
|
||||
|
||||
assertThat(wavePaintCaptor.value.colorFilter).isEqualTo(colorFilter)
|
||||
assertThat(linePaintCaptor.value.colorFilter).isEqualTo(colorFilter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTint() {
|
||||
squigglyProgress.setTint(tint)
|
||||
squigglyProgress.draw(canvas)
|
||||
|
||||
verify(canvas).drawPath(any(), wavePaintCaptor.capture())
|
||||
verify(canvas).drawLine(anyFloat(), anyFloat(), anyFloat(), anyFloat(),
|
||||
linePaintCaptor.capture())
|
||||
|
||||
assertThat(wavePaintCaptor.value.color).isEqualTo(tint)
|
||||
assertThat(linePaintCaptor.value.color).isEqualTo(tint)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user