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

Commit 4623b566 authored by Daniel Norman's avatar Daniel Norman
Browse files

Allows the a11y volume shortcut disambig dialog to show on lock screen.

The edit button is not shown when the dialog is triggerred while on the
lock screen, to prevent editing shortcuts while the device is locked.

Bug: 303871725
Test: Set up 2+ features with volume key shortcut. Press power button to
      go to lockscreen. Trigger the volume key shortcut. Observe dialog
      appears without edit option.
Test: atest AccessibilityShortcutChooserActivityTest
Change-Id: Ia2ec0f9d01ffff0d51d23b491d55ef84d575f09d
parent 7ddc262b
Loading
Loading
Loading
Loading
+8 −1
Original line number Original line Diff line number Diff line
@@ -6,3 +6,10 @@ flag {
    description: "Enable force force-dark for smart inversion and dark theme everywhere"
    description: "Enable force force-dark for smart inversion and dark theme everywhere"
    bug: "282821643"
    bug: "282821643"
}
}

flag {
    namespace: "accessibility"
    name: "allow_shortcut_chooser_on_lockscreen"
    description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen"
    bug: "303871725"
}
+24 −2
Original line number Original line Diff line number Diff line
@@ -28,15 +28,19 @@ import static com.android.internal.accessibility.util.AccessibilityUtils.isUserS
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Bundle;
import android.view.View;
import android.view.View;
import android.view.Window;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.Flags;
import android.widget.AdapterView;
import android.widget.AdapterView;


import com.android.internal.R;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
@@ -207,6 +211,11 @@ public class AccessibilityShortcutChooserActivity extends Activity {
                isEditMenuMode ? this::onTargetChecked : this::onTargetSelected);
                isEditMenuMode ? this::onTargetChecked : this::onTargetSelected);
    }
    }


    @VisibleForTesting
    public AlertDialog getMenuDialog() {
        return mMenuDialog;
    }

    private AlertDialog createMenuDialog() {
    private AlertDialog createMenuDialog() {
        final String dialogTitle =
        final String dialogTitle =
                getString(R.string.accessibility_select_shortcut_menu_title);
                getString(R.string.accessibility_select_shortcut_menu_title);
@@ -216,12 +225,25 @@ public class AccessibilityShortcutChooserActivity extends Activity {
                .setAdapter(mTargetAdapter, /* listener= */ null)
                .setAdapter(mTargetAdapter, /* listener= */ null)
                .setOnDismissListener(dialog -> finish());
                .setOnDismissListener(dialog -> finish());


        if (isUserSetupCompleted(this)) {
        boolean allowEditing = isUserSetupCompleted(this);
        boolean showWhenLocked = false;
        if (Flags.allowShortcutChooserOnLockscreen()) {
            final KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
            if (keyguardManager != null && keyguardManager.isKeyguardLocked()) {
                allowEditing = false;
                showWhenLocked = true;
            }
        }
        if (allowEditing) {
            final String positiveButtonText =
            final String positiveButtonText =
                    getString(R.string.edit_accessibility_shortcut_menu_button);
                    getString(R.string.edit_accessibility_shortcut_menu_button);
            builder.setPositiveButton(positiveButtonText, /* listener= */ null);
            builder.setPositiveButton(positiveButtonText, /* listener= */ null);
        }
        }


        return builder.create();
        final AlertDialog dialog = builder.create();
        if (showWhenLocked) {
            dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
        }
        return dialog;
    }
    }
}
}
+55 −9
Original line number Original line Diff line number Diff line
@@ -29,6 +29,8 @@ import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static androidx.test.espresso.matcher.ViewMatchers.withText;


import static com.google.common.truth.Truth.assertThat;

import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.endsWith;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.any;
@@ -39,6 +41,8 @@ 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;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
@@ -47,11 +51,17 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.Handler;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
import android.support.test.uiautomator.Until;
import android.view.View;
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 androidx.lifecycle.Lifecycle;
import androidx.lifecycle.Lifecycle;
@@ -89,6 +99,9 @@ public class AccessibilityShortcutChooserActivityTest {
    private ActivityScenario<TestAccessibilityShortcutChooserActivity> mScenario;
    private ActivityScenario<TestAccessibilityShortcutChooserActivity> mScenario;
    private TestAccessibilityShortcutChooserActivity mActivity;
    private TestAccessibilityShortcutChooserActivity mActivity;


    @Rule
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();

    @Rule
    @Rule
    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
    @Mock
    @Mock
@@ -101,6 +114,8 @@ public class AccessibilityShortcutChooserActivityTest {
    private ApplicationInfo mApplicationInfo;
    private ApplicationInfo mApplicationInfo;
    @Mock
    @Mock
    private IAccessibilityManager mAccessibilityManagerService;
    private IAccessibilityManager mAccessibilityManagerService;
    @Mock
    private KeyguardManager mKeyguardManager;


    @Before
    @Before
    public void setUp() throws Exception {
    public void setUp() throws Exception {
@@ -116,22 +131,19 @@ public class AccessibilityShortcutChooserActivityTest {
                        Collections.singletonList(mAccessibilityServiceInfo)));
                        Collections.singletonList(mAccessibilityServiceInfo)));
        when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
        when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
                anyString(), anyInt(), anyInt())).thenReturn(true);
                anyString(), anyInt(), anyInt())).thenReturn(true);
        TestAccessibilityShortcutChooserActivity.setupForTesting(mAccessibilityManagerService);
        when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
        mScenario = ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
        TestAccessibilityShortcutChooserActivity.setupForTesting(
        mScenario.onActivity(activity -> mActivity = activity);
                mAccessibilityManagerService, mKeyguardManager);
        mScenario.moveToState(Lifecycle.State.CREATED);
        mScenario.moveToState(Lifecycle.State.STARTED);
        mScenario.moveToState(Lifecycle.State.RESUMED);
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
    }
    }


    @After
    @After
    public void cleanUp() {
    public void cleanUp() {
        mScenario.moveToState(Lifecycle.State.DESTROYED);
        mScenario.close();
    }
    }


    @Test
    @Test
    public void doubleClickTestServiceAndClickDenyButton_permissionDialogDoesNotExist() {
    public void doubleClickTestServiceAndClickDenyButton_permissionDialogDoesNotExist() {
        launchActivity();
        openShortcutsList();
        openShortcutsList();


        // Performing the double-click is flaky so retry if needed.
        // Performing the double-click is flaky so retry if needed.
@@ -154,6 +166,7 @@ public class AccessibilityShortcutChooserActivityTest {
            throws Exception {
            throws Exception {
        when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
        when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
                eq(TEST_COMPONENT_NAME.getPackageName()), anyInt(), anyInt())).thenReturn(false);
                eq(TEST_COMPONENT_NAME.getPackageName()), anyInt(), anyInt())).thenReturn(false);
        launchActivity();
        openShortcutsList();
        openShortcutsList();


        onView(withText(TEST_LABEL)).perform(scrollTo(), click());
        onView(withText(TEST_LABEL)).perform(scrollTo(), click());
@@ -165,6 +178,7 @@ public class AccessibilityShortcutChooserActivityTest {
    @Test
    @Test
    public void popEditShortcutMenuList_oneHandedModeEnabled_shouldBeInListView() {
    public void popEditShortcutMenuList_oneHandedModeEnabled_shouldBeInListView() {
        TestUtils.setOneHandedModeEnabled(this, /* enabled= */ true);
        TestUtils.setOneHandedModeEnabled(this, /* enabled= */ true);
        launchActivity();
        openShortcutsList();
        openShortcutsList();


        onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp());
        onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp());
@@ -176,6 +190,7 @@ public class AccessibilityShortcutChooserActivityTest {
    @Test
    @Test
    public void popEditShortcutMenuList_oneHandedModeDisabled_shouldNotBeInListView() {
    public void popEditShortcutMenuList_oneHandedModeDisabled_shouldNotBeInListView() {
        TestUtils.setOneHandedModeEnabled(this, /* enabled= */ false);
        TestUtils.setOneHandedModeEnabled(this, /* enabled= */ false);
        launchActivity();
        openShortcutsList();
        openShortcutsList();


        onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp());
        onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp());
@@ -184,6 +199,30 @@ public class AccessibilityShortcutChooserActivityTest {
        onView(withText(ONE_HANDED_MODE)).inRoot(isDialog()).check(doesNotExist());
        onView(withText(ONE_HANDED_MODE)).inRoot(isDialog()).check(doesNotExist());
    }
    }


    @Test
    @RequiresFlagsEnabled(Flags.FLAG_ALLOW_SHORTCUT_CHOOSER_ON_LOCKSCREEN)
    public void createDialog_onLockscreen_hasExpectedContent() {
        when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
        launchActivity();

        final AlertDialog dialog = mActivity.getMenuDialog();

        assertThat(dialog.getButton(AlertDialog.BUTTON_POSITIVE).getVisibility())
                .isEqualTo(View.GONE);
        assertThat(dialog.getWindow().getAttributes().flags
                & WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
                .isEqualTo(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
    }

    private void launchActivity() {
        mScenario = ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
        mScenario.onActivity(activity -> mActivity = activity);
        mScenario.moveToState(Lifecycle.State.CREATED);
        mScenario.moveToState(Lifecycle.State.STARTED);
        mScenario.moveToState(Lifecycle.State.RESUMED);
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
    }

    private void openShortcutsList() {
    private void openShortcutsList() {
        UiObject2 editButton = mDevice.findObject(By.text(EDIT_LABEL));
        UiObject2 editButton = mDevice.findObject(By.text(EDIT_LABEL));
        if (editButton != null) {
        if (editButton != null) {
@@ -198,9 +237,13 @@ public class AccessibilityShortcutChooserActivityTest {
    public static class TestAccessibilityShortcutChooserActivity extends
    public static class TestAccessibilityShortcutChooserActivity extends
            AccessibilityShortcutChooserActivity {
            AccessibilityShortcutChooserActivity {
        private static IAccessibilityManager sAccessibilityManagerService;
        private static IAccessibilityManager sAccessibilityManagerService;
        private static KeyguardManager sKeyguardManager;


        public static void setupForTesting(IAccessibilityManager accessibilityManagerService) {
        public static void setupForTesting(
                IAccessibilityManager accessibilityManagerService,
                KeyguardManager keyguardManager) {
            sAccessibilityManagerService = accessibilityManagerService;
            sAccessibilityManagerService = accessibilityManagerService;
            sKeyguardManager = keyguardManager;
        }
        }


        @Override
        @Override
@@ -210,6 +253,9 @@ public class AccessibilityShortcutChooserActivityTest {
                return new AccessibilityManager(this, new Handler(getMainLooper()),
                return new AccessibilityManager(this, new Handler(getMainLooper()),
                        sAccessibilityManagerService, /* userId= */ 0, /* serviceConnect= */ true);
                        sAccessibilityManagerService, /* userId= */ 0, /* serviceConnect= */ true);
            }
            }
            if (Context.KEYGUARD_SERVICE.equals(name)) {
                return sKeyguardManager;
            }


            return super.getSystemService(name);
            return super.getSystemService(name);
        }
        }