Loading core/java/android/view/accessibility/AccessibilityManager.java +25 −1 Original line number Diff line number Diff line Loading @@ -2446,7 +2446,6 @@ public final class AccessibilityManager { } } /** * Attaches a {@link android.view.SurfaceControl} containing an accessibility overlay to the * specified display. Loading @@ -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(); } } } core/java/android/view/accessibility/IAccessibilityManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -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); } core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +2 −0 Original line number Diff line number Diff line Loading @@ -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) Loading packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt +21 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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() Loading @@ -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() = Loading packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractorTest.kt 0 → 100644 +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
core/java/android/view/accessibility/AccessibilityManager.java +25 −1 Original line number Diff line number Diff line Loading @@ -2446,7 +2446,6 @@ public final class AccessibilityManager { } } /** * Attaches a {@link android.view.SurfaceControl} containing an accessibility overlay to the * specified display. Loading @@ -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(); } } }
core/java/android/view/accessibility/IAccessibilityManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -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); }
core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +2 −0 Original line number Diff line number Diff line Loading @@ -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) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt +21 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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() Loading @@ -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() = Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractorTest.kt 0 → 100644 +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) } } }