Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c87de81d authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato
Browse files

Make remote Unfold progress receiver resilient to jank

If the process sending progresses has jank or long main thread pauses, the receiving process would show the same jank pattern.

With this cl, on the receving and a filter is added so smooth those cases, and result in an always smooth animation despite sending process jank

(for now, sending process is always sysui, and receiving process is always launcher)

Bug: 274409068
Test: UnfoldRemoteFilterTest, RemoteUnfoldTransitionReceiverTest and manual
Change-Id: I2cb2e32a8639d364495e7133191e0cd8b8c77649
parent dd5d243b
Loading
Loading
Loading
Loading
+51 −5
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.unfold.progress

import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.SysuiTestCase
import org.junit.Before
import org.junit.Test
@@ -27,23 +28,27 @@ import org.junit.runner.RunWith
@SmallTest
class RemoteUnfoldTransitionReceiverTest : SysuiTestCase() {

    private val progressProvider = RemoteUnfoldTransitionReceiver { it.run() }
    private val progressProvider =
        RemoteUnfoldTransitionReceiver(useReceivingFilter = true) { runOnMainSync(it) }
    private val progressProviderWithoutFilter =
        RemoteUnfoldTransitionReceiver(useReceivingFilter = false) { it.run() }
    private val listener = TestUnfoldProgressListener()

    @Before
    fun setUp() {
        progressProvider.addCallback(listener)
        progressProviderWithoutFilter.addCallback(listener)
    }

    @Test
    fun onTransitionStarted_propagated() {
    fun onTransitionStarted_withFilter_propagated() {
        progressProvider.onTransitionStarted()

        listener.assertStarted()
    }

    @Test
    fun onTransitionProgress_propagated() {
    fun onTransitionProgress_withFilter_propagated() {
        progressProvider.onTransitionStarted()

        progressProvider.onTransitionProgress(0.5f)
@@ -52,7 +57,7 @@ class RemoteUnfoldTransitionReceiverTest : SysuiTestCase() {
    }

    @Test
    fun onTransitionEnded_propagated() {
    fun onTransitionEnded_withFilter_propagated() {
        progressProvider.onTransitionStarted()
        progressProvider.onTransitionProgress(0.5f)

@@ -62,11 +67,52 @@ class RemoteUnfoldTransitionReceiverTest : SysuiTestCase() {
    }

    @Test
    fun onTransitionStarted_afterCallbackRemoved_notPropagated() {
    fun onTransitionStarted_withFilter_afterCallbackRemoved_notPropagated() {
        progressProvider.removeCallback(listener)

        progressProvider.onTransitionStarted()

        listener.assertNotStarted()
    }

    @Test
    fun onTransitionStarted_withoutFilter_propagated() {
        progressProviderWithoutFilter.onTransitionStarted()

        listener.assertStarted()
    }

    @Test
    fun onTransitionProgress_withoutFilter_propagated() {
        progressProviderWithoutFilter.onTransitionStarted()

        progressProviderWithoutFilter.onTransitionProgress(0.5f)

        listener.assertLastProgress(0.5f)
    }

    @Test
    fun onTransitionEnded_withoutFilter_propagated() {
        progressProviderWithoutFilter.onTransitionStarted()
        progressProviderWithoutFilter.onTransitionProgress(0.5f)

        progressProviderWithoutFilter.onTransitionFinished()

        listener.ensureTransitionFinished()
    }

    @Test
    fun onTransitionStarted_withoutFilter_afterCallbackRemoved_notPropagated() {
        progressProviderWithoutFilter.removeCallback(listener)

        progressProviderWithoutFilter.onTransitionStarted()

        listener.assertNotStarted()
    }

    private fun runOnMainSync(f: Runnable) {
        InstrumentationRegistry.getInstrumentation().runOnMainSync { f.run() }
        // Sleep as the animator used from the filter has a callback that happens at every frame.
        Thread.sleep(60)
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -126,7 +126,7 @@ class TestUnfoldProgressListener : UnfoldTransitionProgressProvider.TransitionPr
        }

        fun assertLastProgress(progress: Float) {
            assertThat(progressHistory.last()).isEqualTo(progress)
            assertThat(progressHistory.last()).isWithin(1.0E-4F).of(progress)
        }
    }

+71 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.unfold.progress

import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.SysuiTestCase
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidTestingRunner::class)
@SmallTest
class UnfoldRemoteFilterTest : SysuiTestCase() {
    private val listener = TestUnfoldProgressListener()

    private val progressProvider = UnfoldRemoteFilter(listener)

    @Test
    fun onTransitionStarted_propagated() {
        runOnMainThreadWithInterval({ progressProvider.onTransitionStarted() })
        listener.assertStarted()
    }

    @Test
    fun onTransitionProgress_withInterval_propagated() {
        runOnMainThreadWithInterval(
            { progressProvider.onTransitionStarted() },
            { progressProvider.onTransitionProgress(0.5f) }
        )

        listener.assertLastProgress(0.5f)
    }

    @Test
    fun onTransitionEnded_propagated() {
        runOnMainThreadWithInterval(
            { progressProvider.onTransitionStarted() },
            { progressProvider.onTransitionProgress(0.5f) },
            { progressProvider.onTransitionFinished() },
        )

        listener.ensureTransitionFinished()
    }

    private fun runOnMainThreadWithInterval(
        vararg blocks: () -> Unit,
        interval: Duration = 60.milliseconds
    ) {
        blocks.forEach {
            InstrumentationRegistry.getInstrumentation().runOnMainSync { it() }
            Thread.sleep(interval.inWholeMilliseconds)
        }
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.unfold

import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.dagger.UseReceivingFilter
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver
import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
import dagger.Module
@@ -42,4 +43,6 @@ class UnfoldRemoteModule {
        remoteReceiver.addCallback(traceListener)
        return Optional.of(remoteReceiver)
    }

    @Provides @UseReceivingFilter fun useReceivingFilter(): Boolean = true
}
+20 −0
Original line number Diff line number Diff line
/*
 * 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.unfold.dagger

import javax.inject.Qualifier

/** Annotates whether to use a filter in [RemoteUnfoldTransitionReceiver]. */
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class UseReceivingFilter
Loading