Merge "Fix RippleFinished being called on first draw." into tm-qpr-dev
This commit is contained in:
@@ -21,9 +21,20 @@ import androidx.annotation.VisibleForTesting
|
|||||||
/** Controller that handles playing [RippleAnimation]. */
|
/** Controller that handles playing [RippleAnimation]. */
|
||||||
class MultiRippleController(private val multipleRippleView: MultiRippleView) {
|
class MultiRippleController(private val multipleRippleView: MultiRippleView) {
|
||||||
|
|
||||||
|
private val ripplesFinishedListeners = ArrayList<RipplesFinishedListener>()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** Max number of ripple animations at a time. */
|
/** Max number of ripple animations at a time. */
|
||||||
@VisibleForTesting const val MAX_RIPPLE_NUMBER = 10
|
@VisibleForTesting const val MAX_RIPPLE_NUMBER = 10
|
||||||
|
|
||||||
|
interface RipplesFinishedListener {
|
||||||
|
/** Triggered when all the ripples finish running. */
|
||||||
|
fun onRipplesFinish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addRipplesFinishedListener(listener: RipplesFinishedListener) {
|
||||||
|
ripplesFinishedListeners.add(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Updates all the ripple colors during the animation. */
|
/** Updates all the ripple colors during the animation. */
|
||||||
@@ -38,8 +49,13 @@ class MultiRippleController(private val multipleRippleView: MultiRippleView) {
|
|||||||
|
|
||||||
multipleRippleView.ripples.add(rippleAnimation)
|
multipleRippleView.ripples.add(rippleAnimation)
|
||||||
|
|
||||||
// Remove ripple once the animation is done
|
rippleAnimation.play {
|
||||||
rippleAnimation.play { multipleRippleView.ripples.remove(rippleAnimation) }
|
// Remove ripple once the animation is done
|
||||||
|
multipleRippleView.ripples.remove(rippleAnimation)
|
||||||
|
if (multipleRippleView.ripples.isEmpty()) {
|
||||||
|
ripplesFinishedListeners.forEach { listener -> listener.onRipplesFinish() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger drawing
|
// Trigger drawing
|
||||||
multipleRippleView.invalidate()
|
multipleRippleView.invalidate()
|
||||||
|
|||||||
@@ -33,21 +33,11 @@ class MultiRippleView(context: Context?, attrs: AttributeSet?) : View(context, a
|
|||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||||
val ripples = ArrayList<RippleAnimation>()
|
val ripples = ArrayList<RippleAnimation>()
|
||||||
private val listeners = ArrayList<RipplesFinishedListener>()
|
|
||||||
private val ripplePaint = Paint()
|
private val ripplePaint = Paint()
|
||||||
private var isWarningLogged = false
|
private var isWarningLogged = false
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "MultiRippleView"
|
private const val TAG = "MultiRippleView"
|
||||||
|
|
||||||
interface RipplesFinishedListener {
|
|
||||||
/** Triggered when all the ripples finish running. */
|
|
||||||
fun onRipplesFinish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addRipplesFinishedListener(listener: RipplesFinishedListener) {
|
|
||||||
listeners.add(listener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas?) {
|
override fun onDraw(canvas: Canvas?) {
|
||||||
@@ -76,8 +66,6 @@ class MultiRippleView(context: Context?, attrs: AttributeSet?) : View(context, a
|
|||||||
|
|
||||||
if (shouldInvalidate) {
|
if (shouldInvalidate) {
|
||||||
invalidate()
|
invalidate()
|
||||||
} else { // Nothing is playing.
|
|
||||||
listeners.forEach { listener -> listener.onRipplesFinish() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ public class MediaControlPanel {
|
|||||||
MultiRippleView multiRippleView = vh.getMultiRippleView();
|
MultiRippleView multiRippleView = vh.getMultiRippleView();
|
||||||
mMultiRippleController = new MultiRippleController(multiRippleView);
|
mMultiRippleController = new MultiRippleController(multiRippleView);
|
||||||
mTurbulenceNoiseController = new TurbulenceNoiseController(vh.getTurbulenceNoiseView());
|
mTurbulenceNoiseController = new TurbulenceNoiseController(vh.getTurbulenceNoiseView());
|
||||||
multiRippleView.addRipplesFinishedListener(
|
mMultiRippleController.addRipplesFinishedListener(
|
||||||
() -> {
|
() -> {
|
||||||
if (mTurbulenceNoiseAnimationConfig == null) {
|
if (mTurbulenceNoiseAnimationConfig == null) {
|
||||||
mTurbulenceNoiseAnimationConfig = createLingeringNoiseAnimation();
|
mTurbulenceNoiseAnimationConfig = createLingeringNoiseAnimation();
|
||||||
|
|||||||
@@ -101,4 +101,52 @@ class MultiRippleControllerTest : SysuiTestCase() {
|
|||||||
assertThat(multiRippleView.ripples.size).isEqualTo(0)
|
assertThat(multiRippleView.ripples.size).isEqualTo(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun play_onFinishesAllRipples_triggersRipplesFinished() {
|
||||||
|
var isTriggered = false
|
||||||
|
val listener =
|
||||||
|
object : MultiRippleController.Companion.RipplesFinishedListener {
|
||||||
|
override fun onRipplesFinish() {
|
||||||
|
isTriggered = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
multiRippleController.addRipplesFinishedListener(listener)
|
||||||
|
|
||||||
|
fakeExecutor.execute {
|
||||||
|
multiRippleController.play(RippleAnimation(RippleAnimationConfig(duration = 1000)))
|
||||||
|
multiRippleController.play(RippleAnimation(RippleAnimationConfig(duration = 2000)))
|
||||||
|
|
||||||
|
assertThat(multiRippleView.ripples.size).isEqualTo(2)
|
||||||
|
|
||||||
|
fakeSystemClock.advanceTime(2000L)
|
||||||
|
|
||||||
|
assertThat(multiRippleView.ripples.size).isEqualTo(0)
|
||||||
|
assertThat(isTriggered).isTrue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun play_notAllRipplesFinished_doesNotTriggerRipplesFinished() {
|
||||||
|
var isTriggered = false
|
||||||
|
val listener =
|
||||||
|
object : MultiRippleController.Companion.RipplesFinishedListener {
|
||||||
|
override fun onRipplesFinish() {
|
||||||
|
isTriggered = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
multiRippleController.addRipplesFinishedListener(listener)
|
||||||
|
|
||||||
|
fakeExecutor.execute {
|
||||||
|
multiRippleController.play(RippleAnimation(RippleAnimationConfig(duration = 1000)))
|
||||||
|
multiRippleController.play(RippleAnimation(RippleAnimationConfig(duration = 2000)))
|
||||||
|
|
||||||
|
assertThat(multiRippleView.ripples.size).isEqualTo(2)
|
||||||
|
|
||||||
|
fakeSystemClock.advanceTime(1000L)
|
||||||
|
|
||||||
|
assertThat(multiRippleView.ripples.size).isEqualTo(1)
|
||||||
|
assertThat(isTriggered).isFalse()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
package com.android.systemui.surfaceeffects.ripple
|
|
||||||
|
|
||||||
import android.testing.AndroidTestingRunner
|
|
||||||
import androidx.test.filters.SmallTest
|
|
||||||
import com.android.systemui.SysuiTestCase
|
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor
|
|
||||||
import com.android.systemui.util.time.FakeSystemClock
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
@RunWith(AndroidTestingRunner::class)
|
|
||||||
class MultiRippleViewTest : SysuiTestCase() {
|
|
||||||
private val fakeSystemClock = FakeSystemClock()
|
|
||||||
// FakeExecutor is needed to run animator.
|
|
||||||
private val fakeExecutor = FakeExecutor(fakeSystemClock)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onRippleFinishes_triggersRippleFinished() {
|
|
||||||
val multiRippleView = MultiRippleView(context, null)
|
|
||||||
val multiRippleController = MultiRippleController(multiRippleView)
|
|
||||||
val rippleAnimationConfig = RippleAnimationConfig(duration = 1000L)
|
|
||||||
|
|
||||||
var isTriggered = false
|
|
||||||
val listener =
|
|
||||||
object : MultiRippleView.Companion.RipplesFinishedListener {
|
|
||||||
override fun onRipplesFinish() {
|
|
||||||
isTriggered = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
multiRippleView.addRipplesFinishedListener(listener)
|
|
||||||
|
|
||||||
fakeExecutor.execute {
|
|
||||||
val rippleAnimation = RippleAnimation(rippleAnimationConfig)
|
|
||||||
multiRippleController.play(rippleAnimation)
|
|
||||||
|
|
||||||
fakeSystemClock.advanceTime(rippleAnimationConfig.duration)
|
|
||||||
|
|
||||||
assertThat(isTriggered).isTrue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user