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

Commit 1865dfa0 authored by Riley Jones's avatar Riley Jones Committed by Android (Google) Code Review
Browse files

Merge "Use accessibilityManager#enableShortcutsForTargets when turning shorcuts off/on" into main

parents 573ea5b2 d8324cc8
Loading
Loading
Loading
Loading
+25 −11
Original line number Original line Diff line number Diff line
@@ -24,8 +24,10 @@ import static com.android.internal.accessibility.dialog.AccessibilityTargetHelpe
import static com.android.internal.os.RoSystemProperties.SUPPORT_ONE_HANDED_MODE;
import static com.android.internal.os.RoSystemProperties.SUPPORT_ONE_HANDED_MODE;
import static com.android.internal.util.ArrayUtils.convertToLongArray;
import static com.android.internal.util.ArrayUtils.convertToLongArray;


import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.IntDef;
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ActivityThread;
import android.app.AlertDialog;
import android.app.AlertDialog;
@@ -53,6 +55,7 @@ import android.util.Slog;
import android.view.Window;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.Flags;
import android.widget.Toast;
import android.widget.Toast;


import com.android.internal.R;
import com.android.internal.R;
@@ -328,11 +331,14 @@ public class AccessibilityShortcutController {
        warningToast.show();
        warningToast.show();
    }
    }


    @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
    private AlertDialog createShortcutWarningDialog(int userId) {
    private AlertDialog createShortcutWarningDialog(int userId) {
        List<AccessibilityTarget> targets = getTargets(mContext, HARDWARE);
        List<AccessibilityTarget> targets = getTargets(mContext, HARDWARE);
        if (targets.size() == 0) {
        if (targets.size() == 0) {
            return null;
            return null;
        }
        }
        final AccessibilityManager am = mFrameworkObjectProvider
                .getAccessibilityManagerInstance(mContext);


        // Avoid non-a11y users accidentally turning shortcut on without reading this carefully.
        // Avoid non-a11y users accidentally turning shortcut on without reading this carefully.
        // Put "don't turn on" as the primary action.
        // Put "don't turn on" as the primary action.
@@ -360,11 +366,16 @@ public class AccessibilityShortcutController {
                                // to the Settings.
                                // to the Settings.
                                final ComponentName configDefaultService =
                                final ComponentName configDefaultService =
                                        ComponentName.unflattenFromString(defaultService);
                                        ComponentName.unflattenFromString(defaultService);
                                if (Flags.a11yQsShortcut()) {
                                    am.enableShortcutsForTargets(true, HARDWARE,
                                            Set.of(configDefaultService.flattenToString()), userId);
                                } else {
                                    Settings.Secure.putStringForUser(mContext.getContentResolver(),
                                    Settings.Secure.putStringForUser(mContext.getContentResolver(),
                                            Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
                                            Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
                                            configDefaultService.flattenToString(),
                                            configDefaultService.flattenToString(),
                                            userId);
                                            userId);
                                }
                                }
                            }
                        })
                        })
                .setPositiveButton(R.string.accessibility_shortcut_off,
                .setPositiveButton(R.string.accessibility_shortcut_off,
                        (DialogInterface d, int which) -> {
                        (DialogInterface d, int which) -> {
@@ -373,13 +384,16 @@ public class AccessibilityShortcutController {
                                            mContext,
                                            mContext,
                                            HARDWARE,
                                            HARDWARE,
                                            userId);
                                            userId);

                            if (Flags.a11yQsShortcut()) {
                                am.enableShortcutsForTargets(
                                        false, HARDWARE, targetServices, userId);
                            } else {
                                Settings.Secure.putStringForUser(mContext.getContentResolver(),
                                Settings.Secure.putStringForUser(mContext.getContentResolver(),
                                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "",
                                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "",
                                        userId);
                                        userId);
                                ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
                                ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
                                        mContext, targetServices, userId);
                                        mContext, targetServices, userId);

                            }
                            // If canceled, treat as if the dialog has never been shown
                            // If canceled, treat as if the dialog has never been shown
                            Settings.Secure.putIntForUser(mContext.getContentResolver(),
                            Settings.Secure.putIntForUser(mContext.getContentResolver(),
                                    Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
                                    Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+16 −3
Original line number Original line Diff line number Diff line
@@ -23,11 +23,14 @@ import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueF


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.view.View;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.Flags;


import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
@@ -35,6 +38,8 @@ import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutT
import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;


import java.util.Set;

/**
/**
 * Abstract base class for creating various target related to accessibility service, accessibility
 * Abstract base class for creating various target related to accessibility service, accessibility
 * activity, and allowlisting features.
 * activity, and allowlisting features.
@@ -108,15 +113,23 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS
        }
        }
    }
    }


    @SuppressLint("MissingPermission")
    @Override
    @Override
    public void onCheckedChanged(boolean isChecked) {
    public void onCheckedChanged(boolean isChecked) {
        setShortcutEnabled(isChecked);
        setShortcutEnabled(isChecked);
        if (Flags.a11yQsShortcut()) {
            final AccessibilityManager am =
                    getContext().getSystemService(AccessibilityManager.class);
            am.enableShortcutsForTargets(
                    isChecked, getShortcutType(), Set.of(mId), UserHandle.myUserId());
        } else {
            if (isChecked) {
            if (isChecked) {
                optInValueToSettings(getContext(), getShortcutType(), getId());
                optInValueToSettings(getContext(), getShortcutType(), getId());
            } else {
            } else {
                optOutValueFromSettings(getContext(), getShortcutType(), getId());
                optOutValueFromSettings(getContext(), getShortcutType(), getId());
            }
            }
        }
        }
    }


    public void setStateDescription(CharSequence stateDescription) {
    public void setStateDescription(CharSequence stateDescription) {
        mStateDescription = stateDescription;
        mStateDescription = stateDescription;
+4 −30
Original line number Original line Diff line number Diff line
@@ -17,17 +17,11 @@
package com.android.internal.accessibility.dialog;
package com.android.internal.accessibility.dialog;


import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState;
import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings;


import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.widget.Toast;


import com.android.internal.R;
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;


@@ -47,30 +41,10 @@ class VolumeShortcutToggleAccessibilityServiceTarget extends AccessibilityServic


    @Override
    @Override
    public void onCheckedChanged(boolean isChecked) {
    public void onCheckedChanged(boolean isChecked) {
        switch (getShortcutType()) {
        if (getShortcutType() == HARDWARE) {
            case SOFTWARE:
                onCheckedFromAccessibilityButton(isChecked);
                return;
            case HARDWARE:
            super.onCheckedChanged(isChecked);
            super.onCheckedChanged(isChecked);
                return;
        } else {
            default:
            throw new IllegalStateException("Unexpected shortcut type");
            throw new IllegalStateException("Unexpected shortcut type");
        }
        }
    }
    }

    private void onCheckedFromAccessibilityButton(boolean isChecked) {
        setShortcutEnabled(isChecked);
        final ComponentName componentName = ComponentName.unflattenFromString(getId());
        setAccessibilityServiceState(getContext(), componentName, isChecked);

        if (!isChecked) {
            optOutValueFromSettings(getContext(), UserShortcutType.HARDWARE, getId());

            final String warningText =
                    getContext().getString(R.string.accessibility_uncheck_legacy_item_warning,
                            getLabel());
            Toast.makeText(getContext(), warningText, Toast.LENGTH_SHORT).show();
        }
    }
}
}
+92 −6
Original line number Original line Diff line number Diff line
@@ -64,6 +64,9 @@ import android.os.Build;
import android.os.Handler;
import android.os.Handler;
import android.os.Message;
import android.os.Message;
import android.os.Vibrator;
import android.os.Vibrator;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech;
import android.speech.tts.Voice;
import android.speech.tts.Voice;
@@ -71,6 +74,7 @@ import android.test.mock.MockContentResolver;
import android.view.Window;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.Flags;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.widget.Toast;
import android.widget.Toast;


@@ -83,9 +87,11 @@ import com.android.internal.util.test.FakeSettingsProvider;


import org.junit.AfterClass;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.invocation.InvocationOnMock;
@@ -93,11 +99,14 @@ import org.mockito.invocation.InvocationOnMock;
import java.lang.reflect.Field;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.Set;
import java.util.Set;


@RunWith(AndroidJUnit4.class)
@RunWith(AndroidJUnit4.class)
public class AccessibilityShortcutControllerTest {
public class AccessibilityShortcutControllerTest {
    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
    private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
    private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
    private static final CharSequence PACKAGE_NAME_STRING = "Service name";
    private static final CharSequence PACKAGE_NAME_STRING = "Service name";
    private static final String SERVICE_NAME_SUMMARY = "Summary";
    private static final String SERVICE_NAME_SUMMARY = "Summary";
@@ -126,6 +135,7 @@ public class AccessibilityShortcutControllerTest {
    private @Mock TextToSpeech mTextToSpeech;
    private @Mock TextToSpeech mTextToSpeech;
    private @Mock Voice mVoice;
    private @Mock Voice mVoice;
    private @Mock Ringtone mRingtone;
    private @Mock Ringtone mRingtone;
    private @Captor ArgumentCaptor<List<String>> mListCaptor;


    private MockContentResolver mContentResolver;
    private MockContentResolver mContentResolver;
    private WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
    private WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
@@ -410,6 +420,7 @@ public class AccessibilityShortcutControllerTest {
    }
    }


    @Test
    @Test
    @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void testClickingDisableButtonInDialog_shouldClearShortcutId() throws Exception {
    public void testClickingDisableButtonInDialog_shouldClearShortcutId() throws Exception {
        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
        configureValidShortcutService();
        configureValidShortcutService();
@@ -423,6 +434,29 @@ public class AccessibilityShortcutControllerTest {
                captor.capture());
                captor.capture());
        captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE);
        captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE);


        verify(mAccessibilityManagerService).enableShortcutsForTargets(
                eq(false), eq(HARDWARE), mListCaptor.capture(), anyInt());
        assertThat(mListCaptor.getValue()).containsExactly(SERVICE_NAME_STRING);
        assertThat(Settings.Secure.getInt(
                mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo(
                AccessibilityShortcutController.DialogStatus.NOT_SHOWN);
    }

    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void testClickingDisableButtonInDialog_shouldClearShortcutId_old() throws Exception {
        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
        configureValidShortcutService();
        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
                AccessibilityShortcutController.DialogStatus.NOT_SHOWN);
        getController().performAccessibilityShortcut();

        ArgumentCaptor<DialogInterface.OnClickListener> captor =
                ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
        verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.accessibility_shortcut_off),
                captor.capture());
        captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE);

        assertThat(
        assertThat(
                Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)
                Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)
        ).isEmpty();
        ).isEmpty();
@@ -432,6 +466,8 @@ public class AccessibilityShortcutControllerTest {
    }
    }


    @Test
    @Test
    @EnableFlags(Flags.FLAG_UPDATE_ALWAYS_ON_A11Y_SERVICE)
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void turnOffVolumeShortcutForAlwaysOnA11yService_shouldTurnOffA11yService()
    public void turnOffVolumeShortcutForAlwaysOnA11yService_shouldTurnOffA11yService()
            throws Exception {
            throws Exception {
        configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
        configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
@@ -443,6 +479,8 @@ public class AccessibilityShortcutControllerTest {
    }
    }


    @Test
    @Test
    @EnableFlags(Flags.FLAG_UPDATE_ALWAYS_ON_A11Y_SERVICE)
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void turnOffVolumeShortcutForAlwaysOnA11yService_hasOtherTypesShortcut_shouldNotTurnOffA11yService()
    public void turnOffVolumeShortcutForAlwaysOnA11yService_hasOtherTypesShortcut_shouldNotTurnOffA11yService()
            throws Exception {
            throws Exception {
        configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
        configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
@@ -489,6 +527,7 @@ public class AccessibilityShortcutControllerTest {
    }
    }


    @Test
    @Test
    @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void testTurnOnDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOn()
    public void testTurnOnDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOn()
            throws Exception {
            throws Exception {
        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
@@ -503,8 +542,31 @@ public class AccessibilityShortcutControllerTest {
                captor.capture());
                captor.capture());
        captor.getValue().onClick(null, DialogInterface.BUTTON_NEGATIVE);
        captor.getValue().onClick(null, DialogInterface.BUTTON_NEGATIVE);


        assertThat(
        verify(mAccessibilityManagerService).enableShortcutsForTargets(
                Settings.Secure.getString(mContentResolver,
                eq(true), eq(HARDWARE), mListCaptor.capture(), anyInt());
        assertThat(mListCaptor.getValue()).containsExactly(SERVICE_NAME_STRING);
        assertThat(Settings.Secure.getInt(
                mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo(
                AccessibilityShortcutController.DialogStatus.SHOWN);
    }

    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void testTurnOnDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOn_old()
            throws Exception {
        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
        configureDefaultAccessibilityService();
        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
                AccessibilityShortcutController.DialogStatus.NOT_SHOWN);
        getController().performAccessibilityShortcut();

        ArgumentCaptor<DialogInterface.OnClickListener> captor =
                ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
        verify(mAlertDialogBuilder).setNegativeButton(eq(R.string.accessibility_shortcut_on),
                captor.capture());
        captor.getValue().onClick(null, DialogInterface.BUTTON_NEGATIVE);

        assertThat(Settings.Secure.getString(mContentResolver,
                ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEqualTo(SERVICE_NAME_STRING);
                ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEqualTo(SERVICE_NAME_STRING);
        assertThat(Settings.Secure.getInt(
        assertThat(Settings.Secure.getInt(
                mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo(
                mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo(
@@ -512,6 +574,7 @@ public class AccessibilityShortcutControllerTest {
    }
    }


    @Test
    @Test
    @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void testTurnOffDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOff()
    public void testTurnOffDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOff()
            throws Exception {
            throws Exception {
        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
@@ -526,8 +589,31 @@ public class AccessibilityShortcutControllerTest {
                captor.capture());
                captor.capture());
        captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE);
        captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE);


        assertThat(
        verify(mAccessibilityManagerService).enableShortcutsForTargets(
                Settings.Secure.getString(mContentResolver,
                eq(false), eq(HARDWARE), mListCaptor.capture(), anyInt());
        assertThat(mListCaptor.getValue()).isEmpty();
        assertThat(Settings.Secure.getInt(
                mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo(
                AccessibilityShortcutController.DialogStatus.NOT_SHOWN);
    }

    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void testTurnOffDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOff_old()
            throws Exception {
        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
        configureDefaultAccessibilityService();
        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
                AccessibilityShortcutController.DialogStatus.NOT_SHOWN);
        getController().performAccessibilityShortcut();

        ArgumentCaptor<DialogInterface.OnClickListener> captor =
                ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
        verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.accessibility_shortcut_off),
                captor.capture());
        captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE);

        assertThat(Settings.Secure.getString(mContentResolver,
                ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEmpty();
                ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEmpty();
        assertThat(Settings.Secure.getInt(
        assertThat(Settings.Secure.getInt(
                mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo(
                mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo(
+42 −0
Original line number Original line Diff line number Diff line
@@ -19,8 +19,10 @@ package com.android.internal.accessibility.dialog;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertThat;


import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -31,8 +33,12 @@ import android.content.pm.ParceledListSlice;
import android.os.Handler;
import android.os.Handler;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.Flags;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManager;


import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -47,10 +53,13 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoAnnotations;


import java.util.Collections;
import java.util.Collections;
import java.util.List;


/**
/**
 * Unit Tests for
 * Unit Tests for
@@ -58,10 +67,14 @@ import java.util.Collections;
 */
 */
@RunWith(AndroidJUnit4.class)
@RunWith(AndroidJUnit4.class)
public class InvisibleToggleAccessibilityServiceTargetTest {
public class InvisibleToggleAccessibilityServiceTargetTest {
    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
    @Rule
    @Rule
    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
    @Mock
    @Mock
    private IAccessibilityManager mAccessibilityManagerService;
    private IAccessibilityManager mAccessibilityManagerService;
    @Captor
    private ArgumentCaptor<List<String>> mListCaptor;


    private static final String ALWAYS_ON_SERVICE_PACKAGE_LABEL = "always on a11y service";
    private static final String ALWAYS_ON_SERVICE_PACKAGE_LABEL = "always on a11y service";
    private static final String ALWAYS_ON_SERVICE_COMPONENT_NAME =
    private static final String ALWAYS_ON_SERVICE_COMPONENT_NAME =
@@ -104,6 +117,32 @@ public class InvisibleToggleAccessibilityServiceTargetTest {
    }
    }


    @Test
    @Test
    @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void onCheckedChanged_true_callA11yManagerToUpdateShortcuts() throws Exception {
        mSut.onCheckedChanged(true);

        verify(mAccessibilityManagerService).enableShortcutsForTargets(
                eq(true),
                eq(ShortcutConstants.UserShortcutType.HARDWARE),
                mListCaptor.capture(),
                anyInt());
        assertThat(mListCaptor.getValue()).containsExactly(ALWAYS_ON_SERVICE_COMPONENT_NAME);
    }

    @Test
    @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void onCheckedChanged_false_callA11yManagerToUpdateShortcuts() throws Exception {
        mSut.onCheckedChanged(false);
        verify(mAccessibilityManagerService).enableShortcutsForTargets(
                eq(false),
                eq(ShortcutConstants.UserShortcutType.HARDWARE),
                mListCaptor.capture(),
                anyInt());
        assertThat(mListCaptor.getValue()).containsExactly(ALWAYS_ON_SERVICE_COMPONENT_NAME);
    }

    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void onCheckedChanged_turnOnShortcut_hasOtherShortcut_serviceKeepsOn() {
    public void onCheckedChanged_turnOnShortcut_hasOtherShortcut_serviceKeepsOn() {
        enableA11yService(/* enable= */ true);
        enableA11yService(/* enable= */ true);
        addShortcutForA11yService(
        addShortcutForA11yService(
@@ -116,6 +155,7 @@ public class InvisibleToggleAccessibilityServiceTargetTest {
    }
    }


    @Test
    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void onCheckedChanged_turnOnShortcut_noOtherShortcut_shouldTurnOnService() {
    public void onCheckedChanged_turnOnShortcut_noOtherShortcut_shouldTurnOnService() {
        enableA11yService(/* enable= */ false);
        enableA11yService(/* enable= */ false);
        addShortcutForA11yService(
        addShortcutForA11yService(
@@ -128,6 +168,7 @@ public class InvisibleToggleAccessibilityServiceTargetTest {
    }
    }


    @Test
    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void onCheckedChanged_turnOffShortcut_hasOtherShortcut_serviceKeepsOn() {
    public void onCheckedChanged_turnOffShortcut_hasOtherShortcut_serviceKeepsOn() {
        enableA11yService(/* enable= */ true);
        enableA11yService(/* enable= */ true);
        addShortcutForA11yService(
        addShortcutForA11yService(
@@ -140,6 +181,7 @@ public class InvisibleToggleAccessibilityServiceTargetTest {
    }
    }


    @Test
    @Test
    @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
    public void onCheckedChanged_turnOffShortcut_noOtherShortcut_shouldTurnOffService() {
    public void onCheckedChanged_turnOffShortcut_noOtherShortcut_shouldTurnOffService() {
        enableA11yService(/* enable= */ true);
        enableA11yService(/* enable= */ true);
        addShortcutForA11yService(
        addShortcutForA11yService(