summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Lucas Dupin <dupin@google.com> 2022-03-09 02:42:21 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-03-09 02:42:21 +0000
commit0e7bcd27bdbe78d84a2827804e70bd1ebb72a29f (patch)
treea72fa25c8cd064aedf137c5c3cdfd2a7a756d479
parent9b30f9e9e9a25c06fc0b35fd0eabc43f916af2e6 (diff)
parentdddf4fca6d73a32acd88babba0f8aaa6b4b1e4ec (diff)
Merge "Implement seekbar animation" into tm-dev
-rw-r--r--packages/SystemUI/res-keyguard/drawable/media_squiggly_progress.xml17
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/res/values/styles.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt184
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SquigglyProgressTest.kt118
8 files changed, 391 insertions, 17 deletions
diff --git a/packages/SystemUI/res-keyguard/drawable/media_squiggly_progress.xml b/packages/SystemUI/res-keyguard/drawable/media_squiggly_progress.xml
new file mode 100644
index 000000000000..9e61236aa7df
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/media_squiggly_progress.xml
@@ -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 /> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5ca7285d3f73..c64beb58449c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -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>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f2eaa75496e8..3ae21e08005d 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -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>
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index cf997055c692..57701ab618c9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -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
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index 9cf9c4815b67..329dec24a5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -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
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt b/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
new file mode 100644
index 000000000000..f1058e28863a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
@@ -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
+ }
+} \ No newline at end of file
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 3c2392a30731..7ac15125ea7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
@@ -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
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SquigglyProgressTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SquigglyProgressTest.kt
new file mode 100644
index 000000000000..0787fd65eae1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SquigglyProgressTest.kt
@@ -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)
+ }
+} \ No newline at end of file