1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.media.controls.ui.binder 18 19 import android.animation.Animator 20 import android.animation.ObjectAnimator 21 import android.testing.TestableLooper 22 import android.view.View 23 import android.widget.SeekBar 24 import android.widget.TextView 25 import androidx.test.ext.junit.runners.AndroidJUnit4 26 import androidx.test.filters.SmallTest 27 import com.android.systemui.SysuiTestCase 28 import com.android.systemui.media.controls.ui.drawable.SquigglyProgress 29 import com.android.systemui.media.controls.ui.view.MediaViewHolder 30 import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel 31 import com.android.systemui.res.R 32 import com.google.common.truth.Truth.assertThat 33 import org.junit.Before 34 import org.junit.Rule 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 import org.mockito.Mock 38 import org.mockito.Mockito.verify 39 import org.mockito.Mockito.`when` as whenever 40 import org.mockito.junit.MockitoJUnit 41 42 @SmallTest 43 @RunWith(AndroidJUnit4::class) 44 @TestableLooper.RunWithLooper 45 class SeekBarObserverTest : SysuiTestCase() { 46 47 private val disabledHeight = 1 48 private val enabledHeight = 2 49 50 private lateinit var observer: SeekBarObserver 51 @Mock private lateinit var mockSeekbarAnimator: ObjectAnimator 52 @Mock private lateinit var mockHolder: MediaViewHolder 53 @Mock private lateinit var mockSquigglyProgress: SquigglyProgress 54 private lateinit var seekBarView: SeekBar 55 private lateinit var scrubbingElapsedTimeView: TextView 56 private lateinit var scrubbingTotalTimeView: TextView 57 58 @JvmField @Rule val mockitoRule = MockitoJUnit.rule() 59 60 @Before setUpnull61 fun setUp() { 62 context.orCreateTestableResources.addOverride( 63 R.dimen.qs_media_enabled_seekbar_height, 64 enabledHeight 65 ) 66 context.orCreateTestableResources.addOverride( 67 R.dimen.qs_media_disabled_seekbar_height, 68 disabledHeight 69 ) 70 71 seekBarView = SeekBar(context) 72 seekBarView.progressDrawable = mockSquigglyProgress 73 scrubbingElapsedTimeView = TextView(context) 74 scrubbingTotalTimeView = TextView(context) 75 whenever(mockHolder.seekBar).thenReturn(seekBarView) 76 whenever(mockHolder.scrubbingElapsedTimeView).thenReturn(scrubbingElapsedTimeView) 77 whenever(mockHolder.scrubbingTotalTimeView).thenReturn(scrubbingTotalTimeView) 78 79 observer = 80 object : SeekBarObserver(mockHolder) { 81 override fun buildResetAnimator(targetTime: Int): Animator { 82 return mockSeekbarAnimator 83 } 84 } 85 } 86 87 @Test seekBarGonenull88 fun seekBarGone() { 89 // WHEN seek bar is disabled 90 val isEnabled = false 91 val data = SeekBarViewModel.Progress(isEnabled, false, false, false, null, 0, false) 92 observer.onChanged(data) 93 // THEN seek bar shows just a thin line with no text 94 assertThat(seekBarView.isEnabled()).isFalse() 95 assertThat(seekBarView.getThumb().getAlpha()).isEqualTo(0) 96 assertThat(seekBarView.contentDescription).isEqualTo("") 97 assertThat(seekBarView.maxHeight).isEqualTo(disabledHeight) 98 } 99 100 @Test seekBarVisiblenull101 fun seekBarVisible() { 102 // WHEN seek bar is enabled 103 val isEnabled = true 104 val data = SeekBarViewModel.Progress(isEnabled, true, false, false, 3000, 12000, true) 105 observer.onChanged(data) 106 // THEN seek bar is visible and thick 107 assertThat(seekBarView.getVisibility()).isEqualTo(View.VISIBLE) 108 assertThat(seekBarView.maxHeight).isEqualTo(enabledHeight) 109 } 110 111 @Test seekBarProgressnull112 fun seekBarProgress() { 113 // WHEN part of the track has been played 114 val data = SeekBarViewModel.Progress(true, true, true, false, 3000, 120000, true) 115 observer.onChanged(data) 116 // THEN seek bar shows the progress 117 assertThat(seekBarView.progress).isEqualTo(3000) 118 assertThat(seekBarView.max).isEqualTo(120000) 119 120 val desc = context.getString(R.string.controls_media_seekbar_description, "00:03", "02:00") 121 assertThat(seekBarView.contentDescription).isEqualTo(desc) 122 } 123 124 @Test seekBarDisabledWhenSeekNotAvailablenull125 fun seekBarDisabledWhenSeekNotAvailable() { 126 // WHEN seek is not available 127 val isSeekAvailable = false 128 val data = 129 SeekBarViewModel.Progress(true, isSeekAvailable, false, false, 3000, 120000, false) 130 observer.onChanged(data) 131 // THEN seek bar is not enabled 132 assertThat(seekBarView.isEnabled()).isFalse() 133 } 134 135 @Test seekBarEnabledWhenSeekNotAvailablenull136 fun seekBarEnabledWhenSeekNotAvailable() { 137 // WHEN seek is available 138 val isSeekAvailable = true 139 val data = 140 SeekBarViewModel.Progress(true, isSeekAvailable, false, false, 3000, 120000, false) 141 observer.onChanged(data) 142 // THEN seek bar is not enabled 143 assertThat(seekBarView.isEnabled()).isTrue() 144 } 145 146 @Test seekBarPlayingNotScrubbingnull147 fun seekBarPlayingNotScrubbing() { 148 // WHEN playing 149 val isPlaying = true 150 val isScrubbing = false 151 val data = SeekBarViewModel.Progress(true, true, isPlaying, isScrubbing, 3000, 120000, true) 152 observer.onChanged(data) 153 // THEN progress drawable is animating 154 verify(mockSquigglyProgress).animate = true 155 } 156 157 @Test seekBarNotPlayingNotScrubbingnull158 fun seekBarNotPlayingNotScrubbing() { 159 // WHEN not playing & not scrubbing 160 val isPlaying = false 161 val isScrubbing = false 162 val data = SeekBarViewModel.Progress(true, true, isPlaying, isScrubbing, 3000, 120000, true) 163 observer.onChanged(data) 164 // THEN progress drawable is not animating 165 verify(mockSquigglyProgress).animate = false 166 } 167 168 @Test seekbarNotListeningNotScrubbingPlayingnull169 fun seekbarNotListeningNotScrubbingPlaying() { 170 // WHEN playing 171 val isPlaying = true 172 val isScrubbing = false 173 val data = 174 SeekBarViewModel.Progress(true, true, isPlaying, isScrubbing, 3000, 120000, false) 175 observer.onChanged(data) 176 // THEN progress drawable is not animating 177 verify(mockSquigglyProgress).animate = false 178 } 179 180 @Test seekBarPlayingScrubbingnull181 fun seekBarPlayingScrubbing() { 182 // WHEN playing & scrubbing 183 val isPlaying = true 184 val isScrubbing = true 185 val data = SeekBarViewModel.Progress(true, true, isPlaying, isScrubbing, 3000, 120000, true) 186 observer.onChanged(data) 187 // THEN progress drawable is not animating 188 verify(mockSquigglyProgress).animate = false 189 } 190 191 @Test seekBarNotPlayingScrubbingnull192 fun seekBarNotPlayingScrubbing() { 193 // WHEN playing & scrubbing 194 val isPlaying = false 195 val isScrubbing = true 196 val data = SeekBarViewModel.Progress(true, true, isPlaying, isScrubbing, 3000, 120000, true) 197 observer.onChanged(data) 198 // THEN progress drawable is not animating 199 verify(mockSquigglyProgress).animate = false 200 } 201 202 @Test seekBarProgress_enabledAndScrubbing_timeViewsHaveTimenull203 fun seekBarProgress_enabledAndScrubbing_timeViewsHaveTime() { 204 val isEnabled = true 205 val isScrubbing = true 206 val data = SeekBarViewModel.Progress(isEnabled, true, true, isScrubbing, 3000, 120000, true) 207 208 observer.onChanged(data) 209 210 assertThat(scrubbingElapsedTimeView.text).isEqualTo("00:03") 211 assertThat(scrubbingTotalTimeView.text).isEqualTo("02:00") 212 } 213 214 @Test seekBarProgress_disabledAndScrubbing_timeViewsEmptynull215 fun seekBarProgress_disabledAndScrubbing_timeViewsEmpty() { 216 val isEnabled = false 217 val isScrubbing = true 218 val data = SeekBarViewModel.Progress(isEnabled, true, true, isScrubbing, 3000, 120000, true) 219 220 observer.onChanged(data) 221 222 assertThat(scrubbingElapsedTimeView.text).isEqualTo("") 223 assertThat(scrubbingTotalTimeView.text).isEqualTo("") 224 } 225 226 @Test seekBarProgress_enabledAndNotScrubbing_timeViewsEmptynull227 fun seekBarProgress_enabledAndNotScrubbing_timeViewsEmpty() { 228 val isEnabled = true 229 val isScrubbing = false 230 val data = SeekBarViewModel.Progress(isEnabled, true, true, isScrubbing, 3000, 120000, true) 231 232 observer.onChanged(data) 233 234 assertThat(scrubbingElapsedTimeView.text).isEqualTo("") 235 assertThat(scrubbingTotalTimeView.text).isEqualTo("") 236 } 237 238 @Test seekBarJumpAnimationnull239 fun seekBarJumpAnimation() { 240 val data0 = SeekBarViewModel.Progress(true, true, true, false, 4000, 120000, true) 241 val data1 = SeekBarViewModel.Progress(true, true, true, false, 10, 120000, true) 242 243 // Set initial position of progress bar 244 observer.onChanged(data0) 245 assertThat(seekBarView.progress).isEqualTo(4000) 246 assertThat(seekBarView.max).isEqualTo(120000) 247 248 // Change to second data & confirm no change to position (due to animation delay) 249 observer.onChanged(data1) 250 assertThat(seekBarView.progress).isEqualTo(4000) 251 verify(mockSeekbarAnimator).start() 252 } 253 254 @Test seekbarActive_animationsDisablednull255 fun seekbarActive_animationsDisabled() { 256 // WHEN playing, but animations have been disabled 257 observer.animationEnabled = false 258 val isPlaying = true 259 val isScrubbing = false 260 val data = SeekBarViewModel.Progress(true, true, isPlaying, isScrubbing, 3000, 120000, true) 261 observer.onChanged(data) 262 263 // THEN progress drawable does not animate 264 verify(mockSquigglyProgress).animate = false 265 } 266 } 267