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

Commit 8960fbf3 authored by Chun-Ku Lin's avatar Chun-Ku Lin
Browse files

Auto Add/Remove a11y tiles when the ACCESSIBILITY_QS_TILES updates

Bug: 317295664
Flag: ACONFIG android.view.accessibility.a11y_qs_shortcut STAGING
Test: atest
Change-Id: I8f3de53281bb41128dc30be03ce119e79bff4f40
parent 64fccbba
Loading
Loading
Loading
Loading
+82 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.accessibility.data.repository

import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class AccessibilityQsShortcutsRepositoryImplTest : SysuiTestCase() {
    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)
    private val secureSettings = FakeSettings()

    private val userA11yQsShortcutsRepositoryFactory =
        object : UserA11yQsShortcutsRepository.Factory {
            override fun create(userId: Int): UserA11yQsShortcutsRepository {
                return UserA11yQsShortcutsRepository(
                    userId,
                    secureSettings,
                    testScope.backgroundScope,
                    testDispatcher,
                )
            }
        }

    private val underTest =
        AccessibilityQsShortcutsRepositoryImpl(userA11yQsShortcutsRepositoryFactory)

    @Test
    fun a11yQsShortcutTargetsForCorrectUsers() =
        testScope.runTest {
            val user0 = 0
            val targetsForUser0 = setOf("a", "b", "c")
            val user1 = 1
            val targetsForUser1 = setOf("A")
            val targetsFromUser0 by collectLastValue(underTest.a11yQsShortcutTargets(user0))
            val targetsFromUser1 by collectLastValue(underTest.a11yQsShortcutTargets(user1))

            storeA11yQsShortcutTargetsForUser(targetsForUser0, user0)
            storeA11yQsShortcutTargetsForUser(targetsForUser1, user1)

            assertThat(targetsFromUser0).isEqualTo(targetsForUser0)
            assertThat(targetsFromUser1).isEqualTo(targetsForUser1)
        }

    private fun storeA11yQsShortcutTargetsForUser(a11yQsTargets: Set<String>, forUser: Int) {
        secureSettings.putStringForUser(
            SETTING_NAME,
            a11yQsTargets.joinToString(separator = ":"),
            forUser
        )
    }

    companion object {
        private const val SETTING_NAME = Settings.Secure.ACCESSIBILITY_QS_TARGETS
    }
}
+66 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.accessibility.data.repository

import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class UserA11yQsShortcutsRepositoryTest : SysuiTestCase() {
    private val secureSettings = FakeSettings()
    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)

    private val underTest =
        UserA11yQsShortcutsRepository(
            USER_ID,
            secureSettings,
            testScope.backgroundScope,
            testDispatcher
        )

    @Test
    fun targetsMatchesSetting() =
        testScope.runTest {
            val observedTargets by collectLastValue(underTest.targets)
            val a11yQsTargets = setOf("a", "b", "c")
            secureSettings.putStringForUser(
                SETTING_NAME,
                a11yQsTargets.joinToString(SEPARATOR),
                USER_ID
            )

            assertThat(observedTargets).isEqualTo(a11yQsTargets)
        }

    companion object {
        private const val USER_ID = 0
        private const val SEPARATOR = ":"
        private const val SETTING_NAME = Settings.Secure.ACCESSIBILITY_QS_TARGETS
    }
}
+87 −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.qs.pipeline.domain.autoaddable

import android.content.ComponentName
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.accessibility.Flags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.accessibility.AccessibilityShortcutController
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.ColorCorrectionTile
import com.android.systemui.qs.tiles.ColorInversionTile
import com.android.systemui.qs.tiles.OneHandedModeTile
import com.android.systemui.qs.tiles.ReduceBrightColorsTile
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class A11yShortcutAutoAddableListTest : SysuiTestCase() {

    private val factory =
        object : A11yShortcutAutoAddable.Factory {
            override fun create(
                spec: TileSpec,
                componentName: ComponentName
            ): A11yShortcutAutoAddable {
                return A11yShortcutAutoAddable(mock(), mock(), spec, componentName)
            }
        }

    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    fun getA11yShortcutAutoAddables_withA11yQsShortcutFlagOff_emptyResult() {
        val autoAddables = A11yShortcutAutoAddableList.getA11yShortcutAutoAddables(factory)

        assertThat(autoAddables).isEmpty()
    }

    @Test
    @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    fun getA11yShortcutAutoAddables_withA11yQsShortcutFlagOn_correctAutoAddables() {
        val expected =
            setOf(
                factory.create(
                    TileSpec.create(ColorCorrectionTile.TILE_SPEC),
                    AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME
                ),
                factory.create(
                    TileSpec.create(ColorInversionTile.TILE_SPEC),
                    AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME
                ),
                factory.create(
                    TileSpec.create(OneHandedModeTile.TILE_SPEC),
                    AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME
                ),
                factory.create(
                    TileSpec.create(ReduceBrightColorsTile.TILE_SPEC),
                    AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME
                ),
            )

        val autoAddables = A11yShortcutAutoAddableList.getA11yShortcutAutoAddables(factory)

        assertThat(autoAddables).isNotEmpty()
        assertThat(autoAddables).containsExactlyElementsIn(expected)
    }
}
+175 −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.qs.pipeline.domain.autoaddable

import android.content.ComponentName
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.FakeAccessibilityQsShortcutsRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class A11yShortcutAutoAddableTest : SysuiTestCase() {
    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)

    private val a11yQsShortcutsRepository = FakeAccessibilityQsShortcutsRepository()
    private val underTest =
        A11yShortcutAutoAddable(a11yQsShortcutsRepository, testDispatcher, SPEC, TARGET_COMPONENT)

    @Test
    fun settingNotSet_noSignal() =
        testScope.runTest {
            val signal by collectLastValue(underTest.autoAddSignal(USER_ID))

            assertThat(signal).isNull() // null means no emitted value
        }

    @Test
    fun settingSetWithTarget_addSignal() =
        testScope.runTest {
            val signal by collectLastValue(underTest.autoAddSignal(USER_ID))
            assertThat(signal).isNull()

            a11yQsShortcutsRepository.setA11yQsShortcutTargets(
                USER_ID,
                setOf(TARGET_COMPONENT_FLATTEN)
            )

            assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
        }

    @Test
    fun settingSetWithoutTarget_removeSignal() =
        testScope.runTest {
            val signal by collectLastValue(flow = underTest.autoAddSignal(USER_ID))
            assertThat(signal).isNull()

            a11yQsShortcutsRepository.setA11yQsShortcutTargets(
                USER_ID,
                setOf(OTHER_COMPONENT_FLATTEN)
            )

            assertThat(signal).isEqualTo(AutoAddSignal.Remove(SPEC))
        }

    @Test
    fun settingSetWithMultipleComponents_containsTarget_addSignal() =
        testScope.runTest {
            val signal by collectLastValue(underTest.autoAddSignal(USER_ID))
            assertThat(signal).isNull()

            a11yQsShortcutsRepository.setA11yQsShortcutTargets(
                USER_ID,
                setOf(OTHER_COMPONENT_FLATTEN, TARGET_COMPONENT_FLATTEN)
            )

            assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
        }

    @Test
    fun settingSetWithMultipleComponents_doesNotContainTarget_removeSignal() =
        testScope.runTest {
            val signal by collectLastValue(underTest.autoAddSignal(USER_ID))
            assertThat(signal).isNull()

            a11yQsShortcutsRepository.setA11yQsShortcutTargets(
                USER_ID,
                setOf(OTHER_COMPONENT_FLATTEN, OTHER_COMPONENT_FLATTEN)
            )

            assertThat(signal).isEqualTo(AutoAddSignal.Remove(SPEC))
        }

    @Test
    fun multipleChangesWithTarget_onlyOneAddSignal() =
        testScope.runTest {
            val signals by collectValues(underTest.autoAddSignal(USER_ID))
            assertThat(signals).isEmpty()

            repeat(3) {
                a11yQsShortcutsRepository.setA11yQsShortcutTargets(
                    USER_ID,
                    setOf(TARGET_COMPONENT_FLATTEN)
                )
            }

            assertThat(signals.size).isEqualTo(1)
            assertThat(signals[0]).isEqualTo(AutoAddSignal.Add(SPEC))
        }

    @Test
    fun multipleChangesWithoutTarget_onlyOneRemoveSignal() =
        testScope.runTest {
            val signals by collectValues(underTest.autoAddSignal(USER_ID))
            assertThat(signals).isEmpty()

            repeat(3) {
                a11yQsShortcutsRepository.setA11yQsShortcutTargets(
                    USER_ID,
                    setOf("$OTHER_COMPONENT_FLATTEN$it")
                )
            }

            assertThat(signals.size).isEqualTo(1)
            assertThat(signals[0]).isEqualTo(AutoAddSignal.Remove(SPEC))
        }

    @Test
    fun settingSetWithTargetForUsers_onlySignalInThatUser() =
        testScope.runTest {
            val otherUserId = USER_ID + 1
            val signalTargetUser by collectLastValue(underTest.autoAddSignal(USER_ID))
            val signalOtherUser by collectLastValue(underTest.autoAddSignal(otherUserId))
            assertThat(signalTargetUser).isNull()
            assertThat(signalOtherUser).isNull()

            a11yQsShortcutsRepository.setA11yQsShortcutTargets(
                USER_ID,
                setOf(TARGET_COMPONENT_FLATTEN)
            )

            assertThat(signalTargetUser).isEqualTo(AutoAddSignal.Add(SPEC))
            assertThat(signalOtherUser).isNull()
        }

    @Test
    fun strategyAlways() {
        assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Always)
    }

    companion object {
        private val SPEC = TileSpec.create("spec")
        private val TARGET_COMPONENT = ComponentName("FakePkgName", "FakeClassName")
        private val TARGET_COMPONENT_FLATTEN = TARGET_COMPONENT.flattenToString()
        private val OTHER_COMPONENT_FLATTEN =
            ComponentName("FakePkgName", "OtherClassName").flattenToString()
        private const val USER_ID = 0
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.systemui.accessibility

import com.android.systemui.accessibility.data.repository.AccessibilityQsShortcutsRepository
import com.android.systemui.accessibility.data.repository.AccessibilityQsShortcutsRepositoryImpl
import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository
import com.android.systemui.accessibility.data.repository.ColorCorrectionRepositoryImpl
import com.android.systemui.accessibility.data.repository.ColorInversionRepository
@@ -31,4 +33,9 @@ interface AccessibilityModule {

    @Binds
    fun colorInversionRepository(impl: ColorInversionRepositoryImpl): ColorInversionRepository

    @Binds
    fun accessibilityQsShortcutsRepository(
        impl: AccessibilityQsShortcutsRepositoryImpl
    ): AccessibilityQsShortcutsRepository
}
Loading