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

Commit ea9f3aee authored by Daniel Norman's avatar Daniel Norman Committed by Android (Google) Code Review
Browse files

Merge "Notify AccessibilityManager when the tiles in SysUi changes" into main

parents f2c28047 52f7c029
Loading
Loading
Loading
Loading
+25 −1
Original line number Diff line number Diff line
@@ -2446,7 +2446,6 @@ public final class AccessibilityManager {
        }
    }


    /**
     * Attaches a {@link android.view.SurfaceControl} containing an accessibility overlay to the
     * specified display.
@@ -2470,4 +2469,29 @@ public final class AccessibilityManager {
            throw re.rethrowFromSystemServer();
        }
    }

    /**
     * Notifies that the current a11y tiles in QuickSettings Panel has been changed
     *
     * @param userId            The userId of the user attempts to change the qs panel.
     * @param tileComponentNames A list of Accessibility feature's TileServices' component names
     *                           and the a11y platform tiles' component names
     * @hide
     */
    @RequiresPermission(Manifest.permission.STATUS_BAR_SERVICE)
    public void notifyQuickSettingsTilesChanged(
            @UserIdInt int userId, List<ComponentName> tileComponentNames) {
        final IAccessibilityManager service;
        synchronized (mLock) {
            service = getServiceLocked();
            if (service == null) {
                return;
            }
        }
        try {
            service.notifyQuickSettingsTilesChanged(userId, tileComponentNames);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -140,4 +140,7 @@ interface IAccessibilityManager {

    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW)")
    void attachAccessibilityOverlayToDisplay(int displayId, in SurfaceControl surfaceControl);

    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE)")
    oneway void notifyQuickSettingsTilesChanged(int userId, in List<ComponentName> tileComponentNames);
}
+2 −0
Original line number Diff line number Diff line
@@ -110,6 +110,8 @@ public class AccessibilityShortcutController {
            new ComponentName("com.android.server.accessibility", "OneHandedModeTile");
    public static final ComponentName REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME =
            new ComponentName("com.android.server.accessibility", "ReduceBrightColorsTile");
    public static final ComponentName FONT_SIZE_TILE_COMPONENT_NAME =
            new ComponentName("com.android.server.accessibility", "FontSizeTile");

    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+21 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility.data.repository

import android.provider.Settings
import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -26,13 +27,22 @@ 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.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule

@SmallTest
@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
class AccessibilityQsShortcutsRepositoryImplTest : SysuiTestCase() {
    @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()

    // mocks
    @Mock private lateinit var a11yManager: AccessibilityManager
    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)
    private val secureSettings = FakeSettings()
@@ -49,8 +59,17 @@ class AccessibilityQsShortcutsRepositoryImplTest : SysuiTestCase() {
            }
        }

    private val underTest =
        AccessibilityQsShortcutsRepositoryImpl(userA11yQsShortcutsRepositoryFactory)
    private lateinit var underTest: AccessibilityQsShortcutsRepositoryImpl

    @Before
    fun setUp() {
        underTest =
            AccessibilityQsShortcutsRepositoryImpl(
                a11yManager,
                userA11yQsShortcutsRepositoryFactory,
                testDispatcher
            )
    }

    @Test
    fun a11yQsShortcutTargetsForCorrectUsers() =
+169 −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.qs.pipeline.domain.interactor

import android.content.Context
import android.content.pm.UserInfo
import android.os.UserHandle
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.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.FakeAccessibilityQsShortcutsRepository
import com.android.systemui.qs.FakeQSFactory
import com.android.systemui.qs.pipeline.domain.model.TileModel
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.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock

@SmallTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class AccessibilityTilesInteractorTest : SysuiTestCase() {
    private val USER_0_INFO =
        UserInfo(
            0,
            "zero",
            "",
            UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
        )

    private val USER_1_INFO =
        UserInfo(
            1,
            "one",
            "",
            UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
        )

    private val USER_0_TILES = listOf(TileSpec.create(ColorInversionTile.TILE_SPEC))
    private val USER_1_TILES = listOf(TileSpec.create(ColorCorrectionTile.TILE_SPEC))
    private lateinit var currentTilesInteractor: CurrentTilesInteractor
    private lateinit var a11yQsShortcutsRepository: FakeAccessibilityQsShortcutsRepository
    private lateinit var underTest: AccessibilityTilesInteractor
    private lateinit var currentTiles: MutableStateFlow<List<TileModel>>
    private lateinit var userContext: MutableStateFlow<Context>
    private lateinit var qsFactory: FakeQSFactory
    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)

    @Before
    fun setUp() {
        a11yQsShortcutsRepository = FakeAccessibilityQsShortcutsRepository()

        qsFactory = FakeQSFactory { spec: String ->
            FakeQSTile(userContext.value.userId).also { it.setTileSpec(spec) }
        }
        currentTiles = MutableStateFlow(emptyList())
        userContext = MutableStateFlow(mock(Context::class.java))
        setUser(USER_0_INFO)

        currentTilesInteractor = mock()
        whenever(currentTilesInteractor.currentTiles).thenReturn(currentTiles)
        whenever(currentTilesInteractor.userContext).thenReturn(userContext)
    }

    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    fun currentTilesChanged_a11yQsShortcutFlagOff_nothingHappen() =
        testScope.runTest {
            underTest = createInteractor()

            setTiles(USER_0_TILES)
            runCurrent()

            assertThat(a11yQsShortcutsRepository.notifyA11yManagerTilesChangedRequests).isEmpty()
        }

    @Test
    @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    fun currentTilesChanged_a11yQsShortcutFlagOn_notifyAccessibilityRepository() =
        testScope.runTest {
            underTest = createInteractor()

            setTiles(USER_0_TILES)
            runCurrent()

            val requests = a11yQsShortcutsRepository.notifyA11yManagerTilesChangedRequests
            assertThat(requests).hasSize(1)
            with(requests[0]) {
                assertThat(this.userContext.userId).isEqualTo(USER_0_INFO.id)
                assertThat(this.tiles).isEqualTo(USER_0_TILES)
            }
        }

    @Test
    @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    fun userChanged_a11yQsShortcutFlagOn_notifyAccessibilityRepositoryWithCorrectTilesAndUser() =
        testScope.runTest {
            underTest = createInteractor()
            setTiles(USER_0_TILES)
            runCurrent()

            // Change User and updates corresponding tiles
            setUser(USER_1_INFO)
            runCurrent()
            setTiles(USER_1_TILES)
            runCurrent()

            val requestsForUser1 =
                a11yQsShortcutsRepository.notifyA11yManagerTilesChangedRequests.filter {
                    it.userContext.userId == USER_1_INFO.id
                }
            assertThat(requestsForUser1).hasSize(1)
            assertThat(requestsForUser1[0].tiles).isEqualTo(USER_1_TILES)
        }

    private fun setTiles(tiles: List<TileSpec>) {
        currentTiles.tryEmit(
            tiles.mapNotNull { qsFactory.createTile(it.spec)?.let { it1 -> TileModel(it, it1) } }
        )
    }

    private fun setUser(userInfo: UserInfo) {
        userContext.tryEmit(
            mock(Context::class.java).also {
                whenever(it.userId).thenReturn(userInfo.id)
                whenever(it.user).thenReturn(UserHandle.of(userInfo.id))
            }
        )
    }

    private fun createInteractor(): AccessibilityTilesInteractor {
        return AccessibilityTilesInteractor(
                a11yQsShortcutsRepository,
                testDispatcher,
                testScope.backgroundScope
            )
            .apply { init(currentTilesInteractor) }
    }
}
Loading