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

Commit a1dc761c authored by Svetoslav Ganov's avatar Svetoslav Ganov
Browse files

Adding scroll actions to accessibility node info.

1. Scrolling actions are crucial for enabling a gesture based
   traversal of the UI and specifically scrollable containers
   especially lists and anything backed by an adapter. Since
   accessibility focus can land only attached views, it cannot
   visit views for adapter items not shown on the screen.
   Auto scrolling the list as a result of putting access focus
   ot a list item does not work well since the user may get
   trapped in a long list. Adding an accessibility node provider
   to emit virtual views for one view before the first and one
   after the last is complex and suffers the limitation of trapping
   the user. Accessibility service need an explicit scroll actions
   which may be performed upon an explicit user action. Hence,
   the user is informed for the start/end of the visible part of
   the list and he makes a deliberate choice to scroll. This will
   benefit also people developing Braille devices since they can
   scroll the content without telling the user to stop using the
   Braille controller and take the device out of his pocket to scroll
   and go back to the Braille controller.

NOTE: Without these action large portions of the screen will be
    hard to access since users will have to touch and explore to
    find and scroll the list.

Change-Id: Iafcf54d4967893205872b3649025a4e347a299ed
parent 1bc1b8a5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -25234,6 +25234,8 @@ package android.view.accessibility {
    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
    field public static final int ACTION_SELECT = 4; // 0x4
    field public static final android.os.Parcelable.Creator CREATOR;
    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+24 −0
Original line number Diff line number Diff line
@@ -204,6 +204,16 @@ public class AccessibilityNodeInfo implements Parcelable {
     */
    public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;

    /**
     * Action to scroll the node content forward.
     */
    public static final int ACTION_SCROLL_FORWARD = 0x00001000;

    /**
     * Action to scroll the node content backward.
     */
    public static final int ACTION_SCROLL_BACKWARD = 0x00002000;

    /**
     * Argument for which movement granularity to be used when traversing the node text.
     * <p>
@@ -569,6 +579,16 @@ public class AccessibilityNodeInfo implements Parcelable {
     * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
     * @see AccessibilityNodeInfo#ACTION_SELECT
     * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
     * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS
     * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS
     * @see AccessibilityNodeInfo#ACTION_CLICK
     * @see AccessibilityNodeInfo#ACTION_LONG_CLICK
     * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
     * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
     * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT
     * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT
     * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD
     * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD
     */
    public int getActions() {
        return mActions;
@@ -1578,6 +1598,10 @@ public class AccessibilityNodeInfo implements Parcelable {
                return "ACTION_NEXT_HTML_ELEMENT";
            case ACTION_PREVIOUS_HTML_ELEMENT:
                return "ACTION_PREVIOUS_HTML_ELEMENT";
            case ACTION_SCROLL_FORWARD:
                return "ACTION_SCROLL_FORWARD";
            case ACTION_SCROLL_BACKWARD:
                return "ACTION_SCROLL_BACKWARD";
            default:
                throw new IllegalArgumentException("Unknown action: " + action);
        }
+27 −0
Original line number Diff line number Diff line
@@ -1355,6 +1355,33 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(AbsListView.class.getName());
        if (getFirstVisiblePosition() > 0) {
            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
        }
        if (getLastVisiblePosition() < getCount() - 1) {
            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
        }
    }

    @Override
    public boolean performAccessibilityAction(int action, Bundle arguments) {
        switch (action) {
            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
                if (getLastVisiblePosition() < getCount() - 1) {
                    final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
                    smoothScrollBy(viewportHeight, PositionScroller.SCROLL_DURATION);
                    return true;
                }
            } return false;
            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
                if (mFirstPosition > 0) {
                    final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
                    smoothScrollBy(-viewportHeight, PositionScroller.SCROLL_DURATION);
                    return true;
                }
            } return false;
        }
        return super.performAccessibilityAction(action, arguments);
    }

    /**
+34 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.FocusFinder;
import android.view.InputDevice;
@@ -736,11 +737,43 @@ public class HorizontalScrollView extends FrameLayout {
        awakenScrollBars();
    }

    @Override
    public boolean performAccessibilityAction(int action, Bundle arguments) {
        switch (action) {
            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
                final int viewportWidth = getWidth() - mPaddingLeft - mPaddingRight;
                final int targetScrollX = Math.min(mScrollX + viewportWidth, getScrollRange());
                if (targetScrollX != mScrollX) {
                    smoothScrollTo(targetScrollX, 0);
                    return true;
                }
            } return false;
            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
                final int viewportWidth = getWidth() - mPaddingLeft - mPaddingRight;
                final int targetScrollX = Math.max(0, mScrollX - viewportWidth);
                if (targetScrollX != mScrollX) {
                    smoothScrollTo(targetScrollX, 0);
                    return true;
                }
            } return false;
        }
        return super.performAccessibilityAction(action, arguments);
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(HorizontalScrollView.class.getName());
        info.setScrollable(getScrollRange() > 0);
        final int scrollRange = getScrollRange();
        if (scrollRange > 0) {
            info.setScrollable(true);
            if (mScrollX > 0) {
                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
            }
            if (mScrollX < scrollRange) {
                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
            }
        }
    }

    @Override
+34 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.StrictMode;
import android.util.AttributeSet;
import android.view.FocusFinder;
@@ -739,11 +740,43 @@ public class ScrollView extends FrameLayout {
        awakenScrollBars();
    }

    @Override
    public boolean performAccessibilityAction(int action, Bundle arguments) {
        switch (action) {
            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
                final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop;
                final int targetScrollY = Math.min(mScrollY + viewportHeight, getScrollRange());
                if (targetScrollY != mScrollY) {
                    smoothScrollTo(0, targetScrollY);
                    return true;
                }
            } return false;
            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
                final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop;
                final int targetScrollY = Math.max(mScrollY - viewportHeight, 0);
                if (targetScrollY != mScrollY) {
                    smoothScrollTo(0, targetScrollY);
                    return true;
                }
            } return false;
        }
        return super.performAccessibilityAction(action, arguments);
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(ScrollView.class.getName());
        info.setScrollable(getScrollRange() > 0);
        final int scrollRange = getScrollRange();
        if (scrollRange > 0) {
            info.setScrollable(true);
            if (mScrollY > 0) {
                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
            }
            if (mScrollY < scrollRange) {
                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
            }
        }
    }

    @Override
Loading