diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt index 93e78acc63fdc..8cd8bf6ca1dbb 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt @@ -21,9 +21,20 @@ import androidx.annotation.VisibleForTesting /** Controller that handles playing [RippleAnimation]. */ class MultiRippleController(private val multipleRippleView: MultiRippleView) { + private val ripplesFinishedListeners = ArrayList() + companion object { /** Max number of ripple animations at a time. */ @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. */ @@ -38,8 +49,13 @@ class MultiRippleController(private val multipleRippleView: MultiRippleView) { multipleRippleView.ripples.add(rippleAnimation) - // Remove ripple once the animation is done - rippleAnimation.play { multipleRippleView.ripples.remove(rippleAnimation) } + rippleAnimation.play { + // Remove ripple once the animation is done + multipleRippleView.ripples.remove(rippleAnimation) + if (multipleRippleView.ripples.isEmpty()) { + ripplesFinishedListeners.forEach { listener -> listener.onRipplesFinish() } + } + } // Trigger drawing multipleRippleView.invalidate() diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt index b8dc223af56cb..550d2c6d87320 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt @@ -33,21 +33,11 @@ class MultiRippleView(context: Context?, attrs: AttributeSet?) : View(context, a @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) val ripples = ArrayList() - private val listeners = ArrayList() private val ripplePaint = Paint() private var isWarningLogged = false companion object { 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?) { @@ -76,8 +66,6 @@ class MultiRippleView(context: Context?, attrs: AttributeSet?) : View(context, a if (shouldInvalidate) { invalidate() - } else { // Nothing is playing. - listeners.forEach { listener -> listener.onRipplesFinish() } } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java index df8fb9149f309..c2442d6b07cd4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java @@ -404,7 +404,7 @@ public class MediaControlPanel { MultiRippleView multiRippleView = vh.getMultiRippleView(); mMultiRippleController = new MultiRippleController(multiRippleView); mTurbulenceNoiseController = new TurbulenceNoiseController(vh.getTurbulenceNoiseView()); - multiRippleView.addRipplesFinishedListener( + mMultiRippleController.addRipplesFinishedListener( () -> { if (mTurbulenceNoiseAnimationConfig == null) { mTurbulenceNoiseAnimationConfig = createLingeringNoiseAnimation(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt index 0d19ab1db3909..056e3863f70eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt @@ -101,4 +101,52 @@ class MultiRippleControllerTest : SysuiTestCase() { 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() + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleViewTest.kt deleted file mode 100644 index 2024d53b0212f..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleViewTest.kt +++ /dev/null @@ -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() - } - } -}