diff options
4 files changed, 252 insertions, 24 deletions
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml index ca8426560ccc..440c6e58d581 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml @@ -58,5 +58,8 @@          <intent>              <action android:name="android.intent.action.VOICE_COMMAND" />          </intent> +        <!--intent> +            <action android:name="android.settings.ACCESSIBILITY_SETTINGS" /> +        </intent-->      </queries>  </manifest>
\ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java index 8ca64d2505ce..96ea5b45282b 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java @@ -35,6 +35,7 @@ import android.os.Handler;  import android.os.Looper;  import android.os.SystemClock;  import android.provider.Settings; +import android.util.Log;  import android.view.Display;  import android.view.KeyEvent;  import android.view.MotionEvent; @@ -56,6 +57,8 @@ public class AccessibilityMenuService extends AccessibilityService      public static final String PACKAGE_NAME = AccessibilityMenuService.class.getPackageName();      public static final String INTENT_TOGGLE_MENU = ".toggle_menu";      public static final String INTENT_HIDE_MENU = ".hide_menu"; +    public static final String INTENT_GLOBAL_ACTION = ".global_action"; +    public static final String INTENT_GLOBAL_ACTION_EXTRA = "GLOBAL_ACTION";      private static final String TAG = "A11yMenuService";      private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L; @@ -231,6 +234,22 @@ public class AccessibilityMenuService extends AccessibilityService      }      /** +     * Performs global action and broadcasts an intent indicating the action was performed. +     * This is unnecessary for any current functionality, but is used for testing. +     * Refer to {@code performGlobalAction()}. +     * +     * @param globalAction Global action to be performed. +     * @return {@code true} if successful, {@code false} otherwise. +     */ +    private boolean performGlobalActionInternal(int globalAction) { +        Intent intent = new Intent(PACKAGE_NAME + INTENT_GLOBAL_ACTION); +        intent.putExtra(INTENT_GLOBAL_ACTION_EXTRA, globalAction); +        sendBroadcast(intent); +        Log.i("A11yMenuService", "Broadcasting global action " + globalAction); +        return performGlobalAction(globalAction); +    } + +    /**       * Handles click events of shortcuts.       *       * @param view the shortcut button being clicked. @@ -249,17 +268,17 @@ public class AccessibilityMenuService extends AccessibilityService                      new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS),                      Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);          } else if (viewTag == ShortcutId.ID_POWER_VALUE.ordinal()) { -            performGlobalAction(GLOBAL_ACTION_POWER_DIALOG); +            performGlobalActionInternal(GLOBAL_ACTION_POWER_DIALOG);          } else if (viewTag == ShortcutId.ID_RECENT_VALUE.ordinal()) { -            performGlobalAction(GLOBAL_ACTION_RECENTS); +            performGlobalActionInternal(GLOBAL_ACTION_RECENTS);          } else if (viewTag == ShortcutId.ID_LOCKSCREEN_VALUE.ordinal()) { -            performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN); +            performGlobalActionInternal(GLOBAL_ACTION_LOCK_SCREEN);          } else if (viewTag == ShortcutId.ID_QUICKSETTING_VALUE.ordinal()) { -            performGlobalAction(GLOBAL_ACTION_QUICK_SETTINGS); +            performGlobalActionInternal(GLOBAL_ACTION_QUICK_SETTINGS);          } else if (viewTag == ShortcutId.ID_NOTIFICATION_VALUE.ordinal()) { -            performGlobalAction(GLOBAL_ACTION_NOTIFICATIONS); +            performGlobalActionInternal(GLOBAL_ACTION_NOTIFICATIONS);          } else if (viewTag == ShortcutId.ID_SCREENSHOT_VALUE.ordinal()) { -            performGlobalAction(GLOBAL_ACTION_TAKE_SCREENSHOT); +            performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT);          } else if (viewTag == ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()) {              adjustBrightness(BRIGHTNESS_UP_INCREMENT_GAMMA);              return; diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml index 7be6ca742376..2be92450f207 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml @@ -29,4 +29,15 @@          android:targetPackage="com.android.systemui.accessibility.accessibilitymenu.tests"          android:label="AccessibilityMenu Test Cases">      </instrumentation> + +    <queries> +        <intent> +            <action android:name="android.intent.action.VIEW" /> +            <category android:name="android.intent.category.BROWSABLE" /> +            <data android:scheme="https" /> +        </intent> +        <intent> +            <action android:name="android.intent.action.VOICE_COMMAND" /> +        </intent> +    </queries>  </manifest>
\ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java index 529a70c1ab18..0e89dcdaf142 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java @@ -16,6 +16,15 @@  package com.android.systemui.accessibility.accessibilitymenu.tests; +import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN; +import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS; +import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_POWER_DIALOG; +import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS; +import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_RECENTS; +import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT; + +import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_GLOBAL_ACTION; +import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_GLOBAL_ACTION_EXTRA;  import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_HIDE_MENU;  import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_TOGGLE_MENU;  import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.PACKAGE_NAME; @@ -23,11 +32,15 @@ import static com.android.systemui.accessibility.accessibilitymenu.Accessibility  import android.accessibilityservice.AccessibilityServiceInfo;  import android.app.Instrumentation;  import android.app.UiAutomation; +import android.content.BroadcastReceiver;  import android.content.Context;  import android.content.Intent; +import android.content.IntentFilter;  import android.hardware.display.BrightnessInfo;  import android.hardware.display.DisplayManager; +import android.media.AudioManager;  import android.provider.Settings; +import android.util.Log;  import android.view.accessibility.AccessibilityManager;  import android.view.accessibility.AccessibilityNodeInfo; @@ -37,22 +50,29 @@ import androidx.test.platform.app.InstrumentationRegistry;  import com.android.compatibility.common.util.TestUtils;  import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut.ShortcutId; +import org.junit.After;  import org.junit.AfterClass; +import org.junit.Before;  import org.junit.BeforeClass;  import org.junit.Test;  import org.junit.runner.RunWith;  import java.util.List; +import java.util.concurrent.atomic.AtomicInteger;  @RunWith(AndroidJUnit4.class)  public class AccessibilityMenuServiceTest {      private static final String TAG = "A11yMenuServiceTest"; +    private static final int CLICK_ID = AccessibilityNodeInfo.ACTION_CLICK;      private static final int TIMEOUT_SERVICE_STATUS_CHANGE_S = 5; -    private static final int TIMEOUT_UI_CHANGE_S = 5; +    private static final int TIMEOUT_UI_CHANGE_S = 10; +    private static final int NO_GLOBAL_ACTION = -1; +    private static final String INPUT_KEYEVENT_KEYCODE_BACK = "input keyevent KEYCODE_BACK";      private static Instrumentation sInstrumentation;      private static UiAutomation sUiAutomation; +    private static AtomicInteger sLastGlobalAction;      private static AccessibilityManager sAccessibilityManager; @@ -62,7 +82,7 @@ public class AccessibilityMenuServiceTest {          sInstrumentation = InstrumentationRegistry.getInstrumentation();          sUiAutomation = sInstrumentation.getUiAutomation(                  UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); -        final Context context = sInstrumentation.getContext(); +        final Context context = sInstrumentation.getTargetContext();          sAccessibilityManager = context.getSystemService(AccessibilityManager.class);          // Disable all a11yServices if any are active. @@ -85,6 +105,17 @@ public class AccessibilityMenuServiceTest {                  () -> sAccessibilityManager.getEnabledAccessibilityServiceList(                          AccessibilityServiceInfo.FEEDBACK_ALL_MASK).stream().filter(                                  info -> info.getId().contains(serviceName)).count() == 1); + +        sLastGlobalAction = new AtomicInteger(NO_GLOBAL_ACTION); +        context.registerReceiver(new BroadcastReceiver() { +            @Override +            public void onReceive(Context context, Intent intent) { +                Log.i(TAG, "Received global action intent."); +                sLastGlobalAction.set( +                        intent.getIntExtra(INTENT_GLOBAL_ACTION_EXTRA, NO_GLOBAL_ACTION)); +            }}, +                new IntentFilter(PACKAGE_NAME + INTENT_GLOBAL_ACTION), +                null, null, Context.RECEIVER_EXPORTED);      }      @AfterClass @@ -93,23 +124,39 @@ public class AccessibilityMenuServiceTest {                  Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");      } -    private boolean isMenuVisible() { -        return sUiAutomation.getRootInActiveWindow() != null -                && sUiAutomation.getRootInActiveWindow().getPackageName().toString().equals( -                PACKAGE_NAME); +    @Before +    public void setup() throws Throwable { +        openMenu();      } -    private void openMenu() throws Throwable { -        if (isMenuVisible()) { -            return; -        } +    @After +    public void tearDown() throws Throwable { +        closeMenu(); +        sLastGlobalAction.set(NO_GLOBAL_ACTION); +    } + +    private static boolean isMenuVisible() { +        AccessibilityNodeInfo root = sUiAutomation.getRootInActiveWindow(); +        return root != null && root.getPackageName().toString().equals(PACKAGE_NAME); +    } + +    private static void openMenu() throws Throwable {          Intent intent = new Intent(PACKAGE_NAME + INTENT_TOGGLE_MENU);          sInstrumentation.getContext().sendBroadcast(intent); +          TestUtils.waitUntil("Timed out before menu could appear.", -                TIMEOUT_UI_CHANGE_S, () -> isMenuVisible()); +                TIMEOUT_UI_CHANGE_S, +                () -> { +                    if (isMenuVisible()) { +                        return true; +                    } else { +                        sInstrumentation.getContext().sendBroadcast(intent); +                        return false; +                    } +                });      } -    private void closeMenu() throws Throwable { +    private static void closeMenu() throws Throwable {          if (!isMenuVisible()) {              return;          } @@ -119,11 +166,21 @@ public class AccessibilityMenuServiceTest {                  TIMEOUT_UI_CHANGE_S, () -> !isMenuVisible());      } +    /** +     * Provides list of all present shortcut buttons. +     * @return List of shortcut buttons. +     */      private List<AccessibilityNodeInfo> getGridButtonList() {          return sUiAutomation.getRootInActiveWindow()                          .findAccessibilityNodeInfosByViewId(PACKAGE_NAME + ":id/shortcutIconBtn");      } +    /** +     * Returns the first button whose uniqueID matches the provided text. +     * @param buttons List of buttons. +     * @param text Text to match button's uniqueID to. +     * @return Button whose uniqueID matches text, {@code null} otherwise. +     */      private AccessibilityNodeInfo findGridButtonInfo(              List<AccessibilityNodeInfo> buttons, String text) {          for (AccessibilityNodeInfo button: buttons) { @@ -136,8 +193,6 @@ public class AccessibilityMenuServiceTest {      @Test      public void testAdjustBrightness() throws Throwable { -        openMenu(); -          Context context = sInstrumentation.getTargetContext();          DisplayManager displayManager = context.getSystemService(                  DisplayManager.class); @@ -149,7 +204,6 @@ public class AccessibilityMenuServiceTest {          AccessibilityNodeInfo brightnessDownButton = findGridButtonInfo(buttons,                  String.valueOf(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal())); -        int clickId = AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.getId();          BrightnessInfo brightnessInfo = displayManager.getDisplay(                  context.getDisplayId()).getBrightnessInfo(); @@ -159,7 +213,7 @@ public class AccessibilityMenuServiceTest {                      TIMEOUT_UI_CHANGE_S,                      () -> displayManager.getBrightness(context.getDisplayId())                              == brightnessInfo.brightnessMinimum); -            brightnessUpButton.performAction(clickId); +            brightnessUpButton.performAction(CLICK_ID);              TestUtils.waitUntil("Did not detect an increase in brightness.",                      TIMEOUT_UI_CHANGE_S,                      () -> displayManager.getBrightness(context.getDisplayId()) @@ -170,14 +224,155 @@ public class AccessibilityMenuServiceTest {                      TIMEOUT_UI_CHANGE_S,                      () -> displayManager.getBrightness(context.getDisplayId())                              == brightnessInfo.brightnessMaximum); -            brightnessDownButton.performAction(clickId); +            brightnessDownButton.performAction(CLICK_ID);              TestUtils.waitUntil("Did not detect a decrease in brightness.",                      TIMEOUT_UI_CHANGE_S,                      () -> displayManager.getBrightness(context.getDisplayId())                              < brightnessInfo.brightnessMaximum);          } finally {              displayManager.setBrightness(context.getDisplayId(), resetBrightness); -            closeMenu();          }      } + +    @Test +    public void testAdjustVolume() throws Throwable { +        Context context = sInstrumentation.getTargetContext(); +        AudioManager audioManager = context.getSystemService(AudioManager.class); +        int resetVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); + +        List<AccessibilityNodeInfo> buttons = getGridButtonList(); +        AccessibilityNodeInfo volumeUpButton = findGridButtonInfo(buttons, +                String.valueOf(ShortcutId.ID_VOLUME_UP_VALUE.ordinal())); +        AccessibilityNodeInfo volumeDownButton = findGridButtonInfo(buttons, +                String.valueOf(ShortcutId.ID_VOLUME_DOWN_VALUE.ordinal())); + +        try { +            int min = audioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC); +            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, min, +                    AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE); +            TestUtils.waitUntil("Could not change audio stream to minimum volume.", +                    TIMEOUT_UI_CHANGE_S, +                    () -> audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) == min); +            volumeUpButton.performAction(CLICK_ID); +            TestUtils.waitUntil("Did not detect an increase in volume.", +                    TIMEOUT_UI_CHANGE_S, +                    () -> audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) > min); + +            int max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); +            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, max, +                    AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE); +            TestUtils.waitUntil("Could not change audio stream to maximum volume.", +                    TIMEOUT_UI_CHANGE_S, +                    () -> audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) == max); +            volumeDownButton.performAction(CLICK_ID); +            TestUtils.waitUntil("Did not detect a decrease in volume.", +                    TIMEOUT_UI_CHANGE_S, +                    () -> audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) < max); +        } finally { +            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, +                    resetVolume, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE); +        } +    } + +    @Test +    public void testAssistantButton_opensVoiceAssistant() throws Throwable { +        AccessibilityNodeInfo assistantButton = findGridButtonInfo(getGridButtonList(), +                String.valueOf(ShortcutId.ID_ASSISTANT_VALUE.ordinal())); +        Intent expectedIntent = new Intent(Intent.ACTION_VOICE_COMMAND); +        String expectedPackage = expectedIntent.resolveActivity( +                sInstrumentation.getContext().getPackageManager()).getPackageName(); + +        sUiAutomation.executeAndWaitForEvent( +                () -> assistantButton.performAction(CLICK_ID), +                (event) -> expectedPackage.contains(event.getPackageName()), +                TIMEOUT_UI_CHANGE_S * 1000 +        ); +    } + +    @Test +    public void testAccessibilitySettingsButton_opensAccessibilitySettings() throws Throwable { +        AccessibilityNodeInfo settingsButton = findGridButtonInfo(getGridButtonList(), +                String.valueOf(ShortcutId.ID_A11YSETTING_VALUE.ordinal())); +        Intent expectedIntent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); +        String expectedPackage = expectedIntent.resolveActivity( +                sInstrumentation.getContext().getPackageManager()).getPackageName(); + +        sUiAutomation.executeAndWaitForEvent( +                () -> settingsButton.performAction(CLICK_ID), +                (event) -> expectedPackage.contains(event.getPackageName()), +                TIMEOUT_UI_CHANGE_S * 1000 +        ); +    } + +    @Test +    public void testPowerButton_performsGlobalAction() throws Throwable { +        AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(), +                String.valueOf(ShortcutId.ID_POWER_VALUE.ordinal())); + +        button.performAction(CLICK_ID); +        TestUtils.waitUntil("Did not detect Power action being performed.", +                TIMEOUT_UI_CHANGE_S, +                () -> sLastGlobalAction.compareAndSet( +                        GLOBAL_ACTION_POWER_DIALOG, NO_GLOBAL_ACTION)); +    } + +    @Test +    public void testRecentButton_performsGlobalAction() throws Throwable { +        AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(), +                String.valueOf(ShortcutId.ID_RECENT_VALUE.ordinal())); + +        button.performAction(CLICK_ID); +        TestUtils.waitUntil("Did not detect Recents action being performed.", +                TIMEOUT_UI_CHANGE_S, +                () -> sLastGlobalAction.compareAndSet( +                        GLOBAL_ACTION_RECENTS, NO_GLOBAL_ACTION)); +    } + +    @Test +    public void testLockButton_performsGlobalAction() throws Throwable { +        AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(), +                String.valueOf(ShortcutId.ID_LOCKSCREEN_VALUE.ordinal())); + +        button.performAction(CLICK_ID); +        TestUtils.waitUntil("Did not detect Lock action being performed.", +                TIMEOUT_UI_CHANGE_S, +                () -> sLastGlobalAction.compareAndSet( +                        GLOBAL_ACTION_LOCK_SCREEN, NO_GLOBAL_ACTION)); +    } + +    @Test +    public void testQuickSettingsButton_performsGlobalAction() throws Throwable { +        AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(), +                String.valueOf(ShortcutId.ID_QUICKSETTING_VALUE.ordinal())); + +        button.performAction(CLICK_ID); +        TestUtils.waitUntil("Did not detect Quick Settings action being performed.", +                TIMEOUT_UI_CHANGE_S, +                () -> sLastGlobalAction.compareAndSet( +                        GLOBAL_ACTION_QUICK_SETTINGS, NO_GLOBAL_ACTION)); +    } + +    @Test +    public void testNotificationsButton_performsGlobalAction() throws Throwable { +        AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(), +                String.valueOf(ShortcutId.ID_NOTIFICATION_VALUE.ordinal())); + +        button.performAction(CLICK_ID); +        TestUtils.waitUntil("Did not detect Notifications action being performed.", +                TIMEOUT_UI_CHANGE_S, +                () -> sLastGlobalAction.compareAndSet( +                        GLOBAL_ACTION_NOTIFICATIONS, NO_GLOBAL_ACTION)); +    } + +    @Test +    public void testScreenshotButton_performsGlobalAction() throws Throwable { +        AccessibilityNodeInfo button = findGridButtonInfo(getGridButtonList(), +                String.valueOf(ShortcutId.ID_SCREENSHOT_VALUE.ordinal())); + +        button.performAction(CLICK_ID); +        TestUtils.waitUntil("Did not detect Screenshot action being performed.", +                TIMEOUT_UI_CHANGE_S, +                () -> sLastGlobalAction.compareAndSet( +                        GLOBAL_ACTION_TAKE_SCREENSHOT, NO_GLOBAL_ACTION)); +    }  }  |