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

Commit 4cab6542 authored by Riley Jones's avatar Riley Jones
Browse files

Cleaning up quick settings flag & removing redundancies

With a11y_qs_enabled being removed,
we are now able to remove a large amount of duplicate code regarding shortcuts,
and centralize most of the logic in manager & shortcutUtils.

This change also adds a new fxn to ShortcutUtils that will allow for further cleanup in Settings.

Test: atest com.android.settings.accessibility com.android.internal.accessibility
Bug: 367414968
Flag: EXEMPT flag cleanup

Change-Id: I7e744df9cf8a6c764448d1bdd4d45973c2b5edcb
parent 688353b0
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -181,7 +181,8 @@ public final class AccessibilityTargetHelper {
        final InvisibleToggleAllowListingFeatureTarget magnification =
                new InvisibleToggleAllowListingFeatureTarget(context,
                        shortcutType,
                        isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
                        isShortcutContained(
                                context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
                        MAGNIFICATION_CONTROLLER_NAME,
                        uid,
                        context.getString(R.string.accessibility_magnification_chooser_text),
+34 −11
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController
import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType.INVISIBLE_TOGGLE;
import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE;
@@ -157,19 +158,42 @@ public final class ShortcutUtils {
    }

    /**
     * Returns if a {@code shortcutType} shortcut contains {@code componentId}.
     * Returns if a {@code shortcutType} shortcut contains {@code componentName}.
     *
     * @param context The current context.
     * @param shortcutType The preferred shortcut type user selected.
     * @param componentId The component id that need to be checked.
     * @return {@code true} if a component id is contained.
     * @param componentName The component that need to be checked.
     * @return {@code true} if the shortcut contains {@code componentName}.
     */
    public static boolean isShortcutContained(Context context, @UserShortcutType int shortcutType,
            @NonNull String componentId) {
        final AccessibilityManager am = (AccessibilityManager) context.getSystemService(
                Context.ACCESSIBILITY_SERVICE);
        final List<String> requiredTargets = am.getAccessibilityShortcutTargets(shortcutType);
        return requiredTargets.contains(componentId);
    @SuppressLint("MissingPermission")
    public static boolean isShortcutContained(
            Context context, @UserShortcutType int shortcutType, @NonNull String componentName) {
        AccessibilityManager manager = context.getSystemService(AccessibilityManager.class);
        if (manager != null) {
            return manager
                    .getAccessibilityShortcutTargets(shortcutType).contains(componentName);
        } else {
            return false;
        }
    }

    /**
     * Returns every shortcut type that currently has the provided componentName as a target.
     * Types are returned as a singular flag integer.
     * If none have the componentName, returns {@link UserShortcutType#DEFAULT}
     */
    public static int getEnabledShortcutTypes(
            Context context, String componentName) {
        final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
        if (am == null) return DEFAULT;

        int shortcutTypes = DEFAULT;
        for (int shortcutType : USER_SHORTCUT_TYPES) {
            if (am.getAccessibilityShortcutTargets(shortcutType).contains(componentName)) {
                shortcutTypes |= shortcutType;
            }
        }
        return shortcutTypes;
    }

    /**
@@ -229,8 +253,7 @@ public final class ShortcutUtils {
     */
    public static void updateInvisibleToggleAccessibilityServiceEnableState(
            Context context, Set<String> componentNames, int userId) {
        final AccessibilityManager am = (AccessibilityManager) context.getSystemService(
                Context.ACCESSIBILITY_SERVICE);
        final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
        if (am == null) return;

        final List<AccessibilityServiceInfo> installedServices =
+82 −30
Original line number Diff line number Diff line
@@ -20,8 +20,8 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATI
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;

import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;

import static com.google.common.truth.Truth.assertThat;
@@ -42,19 +42,22 @@ import android.testing.TestableContext;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.accessibility.TestUtils;
import com.android.internal.accessibility.common.ShortcutConstants;

import com.google.testing.junit.testparameterinjector.TestParameter;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
@@ -62,7 +65,7 @@ import java.util.StringJoiner;
/**
 * Unit Tests for {@link com.android.internal.accessibility.util.ShortcutUtils}
 */
@RunWith(AndroidJUnit4.class)
@RunWith(TestParameterInjector.class)
public class ShortcutUtilsTest {
    private static final Set<String> ONE_COMPONENT = Set.of(
            new ComponentName("pkg", "serv").flattenToString());
@@ -99,38 +102,19 @@ public class ShortcutUtilsTest {
    }

    @Test
    public void getShortcutTargets_softwareShortcutNoService_emptyResult() {
        assertThat(
                ShortcutUtils.getShortcutTargetsFromSettings(
                        mContext, SOFTWARE, mDefaultUserId)
        ).isEmpty();
    }

    @Test
    public void getShortcutTargets_volumeKeyShortcutNoService_emptyResult() {
        assertThat(
                ShortcutUtils.getShortcutTargetsFromSettings(
                        mContext, ShortcutConstants.UserShortcutType.HARDWARE,
                        mDefaultUserId)
        ).isEmpty();
    }

    @Test
    public void getShortcutTargets_gestureShortcutNoService_emptyResult() {
        assertThat(
                ShortcutUtils.getShortcutTargetsFromSettings(
                        mContext, GESTURE, mDefaultUserId)
        ).isEmpty();
    }
    public void getShortcutTargets_noService_emptyResult(
            @TestParameter(valuesProvider = ShortcutTypeValueProvider.class) int shortcutType) {
        Settings.Secure.putStringForUser(
                mContext.getContentResolver(),
                ShortcutUtils.convertToKey(shortcutType), "", mContext.getUserId());

    @Test
    public void getShortcutTargets_keyGestureShortcutNoService_emptyResult() {
        assertThat(
                ShortcutUtils.getShortcutTargetsFromSettings(
                        mContext, KEY_GESTURE, mDefaultUserId)
                        mContext, shortcutType, mDefaultUserId)
        ).isEmpty();
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void getShortcutTargets_softwareShortcut1Service_return1Service() {
        setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
@@ -143,6 +127,7 @@ public class ShortcutUtilsTest {
        ).containsExactlyElementsIn(ONE_COMPONENT);
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void getShortcutTargets_volumeShortcut2Service_return2Service() {
        setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
@@ -155,6 +140,7 @@ public class ShortcutUtilsTest {
        ).containsExactlyElementsIn(TWO_COMPONENTS);
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void getShortcutTargets_tripleTapShortcut_magnificationDisabled_emptyResult() {
        enableTripleTapShortcutForMagnification(/* enable= */ false);
@@ -168,6 +154,7 @@ public class ShortcutUtilsTest {
        ).isEmpty();
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void getShortcutTargets_tripleTapShortcut_magnificationEnabled_returnMagnification() {
        setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
@@ -181,6 +168,7 @@ public class ShortcutUtilsTest {
        ).containsExactly(ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER);
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOn_noShortcuts_serviceTurnedOff() {
        setupA11yServiceAndShortcutState(
@@ -195,6 +183,7 @@ public class ShortcutUtilsTest {
        assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ false);
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOnForBothUsers_noShortcutsForGuestUser_serviceTurnedOffForGuestUserOnly() {
        // setup arbitrary userId by add 10 to the default user id
@@ -218,6 +207,7 @@ public class ShortcutUtilsTest {
                ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ true, mDefaultUserId);
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOn_hasShortcut_serviceKeepsOn() {
        setupA11yServiceAndShortcutState(
@@ -232,6 +222,7 @@ public class ShortcutUtilsTest {
        assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ true);
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOff_noShortcuts_serviceKeepsOff() {
        setupA11yServiceAndShortcutState(
@@ -246,6 +237,7 @@ public class ShortcutUtilsTest {
        assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ false);
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOff_hasShortcuts_serviceTurnsOn() {
        setupA11yServiceAndShortcutState(
@@ -260,6 +252,7 @@ public class ShortcutUtilsTest {
        assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ true);
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOn_noShortcuts_serviceKeepsOn() {
        setupA11yServiceAndShortcutState(
@@ -274,6 +267,7 @@ public class ShortcutUtilsTest {
        assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ true);
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOn_hasShortcuts_serviceKeepsOn() {
        setupA11yServiceAndShortcutState(
@@ -288,6 +282,7 @@ public class ShortcutUtilsTest {
        assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ true);
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOff_noShortcuts_serviceKeepsOff() {
        setupA11yServiceAndShortcutState(
@@ -302,6 +297,7 @@ public class ShortcutUtilsTest {
        assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ false);
    }

    // TODO 385186274: Parameterize this test.
    @Test
    public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOff_hasShortcuts_serviceKeepsOff() {
        setupA11yServiceAndShortcutState(
@@ -316,6 +312,37 @@ public class ShortcutUtilsTest {
        assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ false);
    }

    @Test
    public void getEnabledShortcutTypes_oneShortcut_returnsExpectedType(
            @TestParameter(valuesProvider = ShortcutTypeValueProvider.class) int shortcutType)
            throws RemoteException {
        clearMockShortcutTypes();
        assertThat(ShortcutUtils.getEnabledShortcutTypes(
                mContext, STANDARD_SERVICE_COMPONENT_NAME)).isEqualTo(DEFAULT);
        mockShortcutType(shortcutType, STANDARD_SERVICE_COMPONENT_NAME);
        assertThat(ShortcutUtils.getEnabledShortcutTypes(
                mContext, STANDARD_SERVICE_COMPONENT_NAME)).isEqualTo(shortcutType);

    }

    @Test
    public void getEnabledShortcutTypes_twoShortcuts_returnsExpectedTypes(
            @TestParameter(valuesProvider = ShortcutTypeValueProvider.class) int shortcutType1,
            @TestParameter(valuesProvider = ShortcutTypeValueProvider.class) int shortcutType2
    ) throws RemoteException {
        if (shortcutType1 == shortcutType2) {
            return;
        }
        clearMockShortcutTypes();
        assertThat(ShortcutUtils.getEnabledShortcutTypes(
                mContext, STANDARD_SERVICE_COMPONENT_NAME)).isEqualTo(DEFAULT);
        mockShortcutType(shortcutType1, STANDARD_SERVICE_COMPONENT_NAME);
        mockShortcutType(shortcutType2, STANDARD_SERVICE_COMPONENT_NAME);
        assertThat(ShortcutUtils.getEnabledShortcutTypes(
                mContext, STANDARD_SERVICE_COMPONENT_NAME)).isEqualTo(
                shortcutType1 | shortcutType2);
    }

    private void setupShortcutTargets(Set<String> components, String shortcutSettingsKey) {
        final StringJoiner stringJoiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR));
        for (String target : components) {
@@ -403,4 +430,29 @@ public class ShortcutUtilsTest {
                add ? a11yServiceComponentName : "",
                userId);
    }

    private void clearMockShortcutTypes() throws RemoteException {
        for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) {
            when(mAccessibilityManagerService
                    .getAccessibilityShortcutTargets(shortcutType)).thenReturn(List.of());
        }
    }

    private void mockShortcutType(int shortcutType, String componentName)
            throws RemoteException {
        when(mAccessibilityManagerService.getAccessibilityShortcutTargets(shortcutType))
                .thenReturn(List.of(componentName));
    }

    static final class ShortcutTypeValueProvider implements
            TestParameter.TestParameterValuesProvider {
        @Override
        public List<Integer> provideValues() {
            List<Integer> values = new ArrayList<>();
            for (int shortcutType: USER_SHORTCUT_TYPES) {
                values.add(shortcutType);
            }
            return values;
        }
    }
}
+7 −19
Original line number Diff line number Diff line
@@ -17,9 +17,6 @@
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
@@ -43,44 +40,35 @@ class A11yShortcutAutoAddableListTest : SysuiTestCase() {
        object : A11yShortcutAutoAddable.Factory {
            override fun create(
                spec: TileSpec,
                componentName: ComponentName
                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() {
    fun getA11yShortcutAutoAddables_correctAutoAddables() {
        val expected =
            setOf(
                factory.create(
                    TileSpec.create(ColorCorrectionTile.TILE_SPEC),
                    AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME
                    AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME,
                ),
                factory.create(
                    TileSpec.create(ColorInversionTile.TILE_SPEC),
                    AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME
                    AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME,
                ),
                factory.create(
                    TileSpec.create(OneHandedModeTile.TILE_SPEC),
                    AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME
                    AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME,
                ),
                factory.create(
                    TileSpec.create(ReduceBrightColorsTile.TILE_SPEC),
                    AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME
                    AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME,
                ),
                factory.create(
                    TileSpec.create(HearingDevicesTile.TILE_SPEC),
                    AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME
                    AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME,
                ),
            )

+0 −121
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.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.coroutines.collectLastValue
import com.android.systemui.qs.ReduceBrightColorsController
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.android.systemui.qs.tiles.ReduceBrightColorsTile
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestResult
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.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class ReduceBrightColorsAutoAddableTest : SysuiTestCase() {

    @Mock private lateinit var reduceBrightColorsController: ReduceBrightColorsController
    @Captor
    private lateinit var reduceBrightColorsListenerCaptor:
        ArgumentCaptor<ReduceBrightColorsController.Listener>

    private lateinit var underTest: ReduceBrightColorsAutoAddable

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
    }

    @Test
    fun notAvailable_strategyDisabled() =
        testWithFeatureAvailability(available = false) {
            assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Disabled)
        }

    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    fun available_strategyIfNotAdded() =
        testWithFeatureAvailability(available = true) {
            assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.IfNotAdded(SPEC))
        }

    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    fun activated_addSignal() = testWithFeatureAvailability {
        val signal by collectLastValue(underTest.autoAddSignal(0))
        runCurrent()

        verify(reduceBrightColorsController).addCallback(capture(reduceBrightColorsListenerCaptor))

        reduceBrightColorsListenerCaptor.value.onActivated(true)

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

    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    fun notActivated_noSignal() = testWithFeatureAvailability {
        val signal by collectLastValue(underTest.autoAddSignal(0))
        runCurrent()

        verify(reduceBrightColorsController).addCallback(capture(reduceBrightColorsListenerCaptor))

        reduceBrightColorsListenerCaptor.value.onActivated(false)

        assertThat(signal).isNull()
    }

    @Test
    @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    fun available_a11yQsShortcutFlagEnabled_strategyDisabled() =
        testWithFeatureAvailability(available = true) {
            assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Disabled)
        }

    private fun testWithFeatureAvailability(
        available: Boolean = true,
        body: suspend TestScope.() -> TestResult
    ) = runTest {
        underTest = ReduceBrightColorsAutoAddable(reduceBrightColorsController, available)
        body()
    }

    companion object {
        private val SPEC by lazy { TileSpec.create(ReduceBrightColorsTile.TILE_SPEC) }
    }
}
Loading