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

Commit fcd75ab5 authored by Brad Hinegardner's avatar Brad Hinegardner
Browse files

Move listener additions within AccessibilityRepository to background

We should specify our own background Handlers and Executors
so that the binder calls don't happen on the main thread.

The overloaded signature without these default to the main
thread, without being obvious they are doing so.

The tests also now force a non-nulls to be passed too.

Fixes: 409473208
Test: atest AccessibilityRepositoryTest.kt
Flag: EXEMPT bugfix
Change-Id: Ie22cf8877bf585d1a7d0ebf28f368c288024a4e8
parent aece73ca
Loading
Loading
Loading
Loading
+43 −7
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility.data.repository

import android.accessibilityservice.AccessibilityServiceInfo
import android.os.Handler
import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -28,11 +29,13 @@ import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.verify
@@ -51,8 +54,19 @@ class AccessibilityRepositoryTest : SysuiTestCase() {
    private val testScope = testKosmos.testScope
    private val backgroundScope = testKosmos.backgroundScope

    @Mock private lateinit var bgExecutor: Executor

    @Mock private lateinit var bgHandler: Handler

    // real impls
    private val underTest by lazy { AccessibilityRepository(a11yManager, backgroundScope) }
    private val underTest by lazy {
        AccessibilityRepository(
            a11yManager = a11yManager,
            backgroundExecutor = bgExecutor,
            backgroundHandler = bgHandler,
            backgroundScope = backgroundScope,
        )
    }

    @Test
    fun isTouchExplorationEnabled_reflectsA11yManager_initFalse() = runTest {
@@ -73,7 +87,10 @@ class AccessibilityRepositoryTest : SysuiTestCase() {
        whenever(a11yManager.isTouchExplorationEnabled).thenReturn(false)
        val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled)
        runCurrent()
        withArgCaptor { verify(a11yManager).addTouchExplorationStateChangeListener(capture()) }
        withArgCaptor {
                verify(a11yManager)
                    .addTouchExplorationStateChangeListener(capture(), ArgumentMatchers.notNull())
            }
            .onTouchExplorationStateChanged(/* enabled= */ true)
        assertThat(isTouchExplorationEnabled).isTrue()
    }
@@ -83,7 +100,10 @@ class AccessibilityRepositoryTest : SysuiTestCase() {
        whenever(a11yManager.isTouchExplorationEnabled).thenReturn(true)
        val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled)
        runCurrent()
        withArgCaptor { verify(a11yManager).addTouchExplorationStateChangeListener(capture()) }
        withArgCaptor {
                verify(a11yManager)
                    .addTouchExplorationStateChangeListener(capture(), ArgumentMatchers.notNull())
            }
            .onTouchExplorationStateChanged(/* enabled= */ false)
        assertThat(isTouchExplorationEnabled).isFalse()
    }
@@ -105,7 +125,11 @@ class AccessibilityRepositoryTest : SysuiTestCase() {
            val isEnabledFiltered by collectLastValue(underTest.isEnabledFiltered)
            runCurrent()
            withArgCaptor {
                    verify(a11yManager).addAccessibilityServicesStateChangeListener(capture())
                    verify(a11yManager)
                        .addAccessibilityServicesStateChangeListener(
                            ArgumentMatchers.notNull(),
                            capture(),
                        )
                }
                .onAccessibilityServicesStateChanged(a11yManager)
            assertThat(isEnabledFiltered).isFalse()
@@ -117,7 +141,11 @@ class AccessibilityRepositoryTest : SysuiTestCase() {
            val isEnabledFiltered2 by collectLastValue(underTest.isEnabledFiltered)
            runCurrent()
            withArgCaptor {
                    verify(a11yManager).addAccessibilityServicesStateChangeListener(capture())
                    verify(a11yManager)
                        .addAccessibilityServicesStateChangeListener(
                            ArgumentMatchers.notNull(),
                            capture(),
                        )
                }
                .onAccessibilityServicesStateChanged(a11yManager)
            assertThat(isEnabledFiltered2).isTrue()
@@ -132,7 +160,11 @@ class AccessibilityRepositoryTest : SysuiTestCase() {
            val isEnabledFiltered by collectLastValue(underTest.isEnabledFiltered)
            runCurrent()
            withArgCaptor {
                    verify(a11yManager).addAccessibilityServicesStateChangeListener(capture())
                    verify(a11yManager)
                        .addAccessibilityServicesStateChangeListener(
                            ArgumentMatchers.notNull(),
                            capture(),
                        )
                }
                .onAccessibilityServicesStateChanged(a11yManager)
            assertThat(isEnabledFiltered).isTrue()
@@ -144,7 +176,11 @@ class AccessibilityRepositoryTest : SysuiTestCase() {
            val isEnabledFiltered2 by collectLastValue(underTest.isEnabledFiltered)
            runCurrent()
            withArgCaptor {
                    verify(a11yManager).addAccessibilityServicesStateChangeListener(capture())
                    verify(a11yManager)
                        .addAccessibilityServicesStateChangeListener(
                            ArgumentMatchers.notNull(),
                            capture(),
                        )
                }
                .onAccessibilityServicesStateChanged(a11yManager)
            assertThat(isEnabledFiltered2).isFalse()
+24 −8
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility.data.repository

import android.accessibilityservice.AccessibilityServiceInfo
import android.os.Handler
import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
import com.android.app.tracing.FlowTracing.tracedAwaitClose
@@ -24,6 +25,7 @@ import com.android.app.tracing.FlowTracing.tracedConflatedCallbackFlow
import com.android.systemui.dagger.qualifiers.Background
import dagger.Module
import dagger.Provides
import java.util.concurrent.Executor
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
@@ -48,8 +50,16 @@ interface AccessibilityRepository {
    companion object {
        operator fun invoke(
            a11yManager: AccessibilityManager,
            @Background backgroundExecutor: Executor,
            @Background backgroundHandler: Handler,
            @Background backgroundScope: CoroutineScope,
        ): AccessibilityRepository = AccessibilityRepositoryImpl(a11yManager, backgroundScope)
        ): AccessibilityRepository =
            AccessibilityRepositoryImpl(
                a11yManager,
                backgroundExecutor,
                backgroundHandler,
                backgroundScope,
            )
    }
}

@@ -57,12 +67,14 @@ private const val TAG = "AccessibilityRepository"

private class AccessibilityRepositoryImpl(
    private val manager: AccessibilityManager,
    @Background private val scope: CoroutineScope,
    @Background private val bgExecutor: Executor,
    @Background private val bgHandler: Handler,
    @Background private val bgScope: CoroutineScope,
) : AccessibilityRepository {
    override val isTouchExplorationEnabled: Flow<Boolean> =
        tracedConflatedCallbackFlow(TAG) {
                val listener = TouchExplorationStateChangeListener(::trySend)
                manager.addTouchExplorationStateChangeListener(listener)
                manager.addTouchExplorationStateChangeListener(listener, bgHandler)
                trySend(manager.isTouchExplorationEnabled)
                tracedAwaitClose(TAG) {
                    manager.removeTouchExplorationStateChangeListener(listener)
@@ -73,7 +85,7 @@ private class AccessibilityRepositoryImpl(
    override val isEnabled: Flow<Boolean> =
        tracedConflatedCallbackFlow(TAG) {
                val listener = AccessibilityManager.AccessibilityStateChangeListener(::trySend)
                manager.addAccessibilityStateChangeListener(listener)
                manager.addAccessibilityStateChangeListener(listener, bgHandler)
                trySend(manager.isEnabled)
                tracedAwaitClose(TAG) { manager.removeAccessibilityStateChangeListener(listener) }
            }
@@ -96,12 +108,12 @@ private class AccessibilityRepositoryImpl(
                                .isNotEmpty()
                        )
                    }
                manager.addAccessibilityServicesStateChangeListener(listener)
                manager.addAccessibilityServicesStateChangeListener(bgExecutor, listener)
                tracedAwaitClose(TAG) {
                    manager.removeAccessibilityServicesStateChangeListener(listener)
                }
            }
            .stateIn(scope = scope, started = SharingStarted.Eagerly, initialValue = false)
            .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)

    override fun getRecommendedTimeout(originalTimeout: Duration, uiFlags: Int): Duration {
        return manager
@@ -113,6 +125,10 @@ private class AccessibilityRepositoryImpl(
@Module
object AccessibilityRepositoryModule {
    @Provides
    fun provideRepo(manager: AccessibilityManager, @Background backgroundScope: CoroutineScope) =
        AccessibilityRepository(manager, backgroundScope)
    fun provideRepo(
        manager: AccessibilityManager,
        @Background backgroundExecutor: Executor,
        @Background backgroundHandler: Handler,
        @Background backgroundScope: CoroutineScope,
    ) = AccessibilityRepository(manager, backgroundExecutor, backgroundHandler, backgroundScope)
}