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

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

Accessibility focus - framework

Usefulness: Keep track of the current user location in the screen when
            traversing the it. Enabling structural and directional
            navigation over all elements on the screen. This enables
            blind users that know the application layout to efficiently
            locate desired elements as opposed to try touch exploring the
            region where the the element should be - very tedious.

Rationale: There are two ways to implement accessibility focus One is
           to let accessibility services keep track of it since they
           have access to the screen content, and another to let the view
           hierarchy keep track of it. While the first approach would
           require almost no work on our part it poses several challenges
           which make it a sub-optimal choice. Having the accessibility focus
           in the accessibility service would require that service to scrape
           the window content every time it changes to sync the view tree
           state and the accessibility focus location. Pretty much the service
           will have to keep an off screen model of the screen content. This
           could be quite challenging to get right and would incur performance
           cost for the multiple IPCs to repeatedly fetch the screen content.
           Further, keeping virtual accessibility focus (i.e. in the service)
           would require sync of the input and accessibility focus. This could
           be challenging to implement right as well. Also, having an unlimited
           number of accessibility services we cannot guarantee that they will
           have a proper implementation, if any, to allow users to perform structural
           navigation of the screen content. Assuming two accessibility
           services implement structural navigation via accessibility focus,
           there is not guarantee that they will behave similarly by default,
           i.e. provide some standard way to navigate the screen content.
           Also feedback from experienced accessibility researchers, specifically
           T.V Raman, provides evidence that having virtual accessibility focus
           creates many issues and it is very hard to get right.
           Therefore, keeping accessibility focus in the system will avoid
           keeping an off-screen model in accessibility services, it will always
           be in sync with the state of the view hierarchy and the input focus.
           Also this will allow having a default behavior for traversing the
           screen via this accessibility focus that is consistent in all
           accessibility services. We provide accessibility services with APIs to
           override this behavior but all of them will perform screen traversal
           in a consistent way by default.

Behavior:  If accessibility is enabled the accessibility focus is the leading one
           and the input follows it. Putting accessibility focus on a view moves
           the input focus there. Clearing the accessibility focus of a view, clears
           the input focus of this view. If accessibility focus is on a view that
           cannot take input focus, then no other view should have input focus.
           In accessibility mode we initially give accessibility focus to the topmost
           view and no view has input focus. This ensures consistent behavior accross
           all apps. Note that accessibility focus can move hierarchically in the
           view tree and having it at the root is better than putting it where the
           input focus would be - at the first input focusable which could be at
           an arbitrary depth in the view tree. By default not all views are reported
           for accessibility, only the important ones. A view may be explicitly labeled
           as important or not for accessibility, or the system determines which one
           is such - default. Important views for accessibility are all views that are
           not dumb layout managers used only to arrange their chidren. Since the same
           content arrangement can be obtained via different combintation of layout
           managers, such managers cannot be used to reliably determine the application
           structure. For example, a user should see a list as a list view with several
           list items and each list item as a text view and a button as opposed to seeing
           all the layout managers used to arrange the list item's content.
           By default only important for accessibility views are regared for accessibility
           purposes. View not regarded for accessibility neither fire accessibility events,
           nor are reported being on the screen. An accessibility service may request the
           system to regard all views. If the target SDK of an accessibility services is
           less than JellyBean, then all views are regarded for accessibility.
           Note that an accessibility service that requires all view to be ragarded for
           accessibility may put accessibility focus on any view. Hence, it may implement
           any navigational paradigm if desired. Especially considering the fact that
           the system is detecting some standard gestures and delegates their processing
           to an accessibility service. The default implementation of an accessibility
           services performs the defualt navigation.

bug:5932640
bug:5605641

Change-Id: Ieac461d480579d706a847b9325720cb254736ebe
parent dbed083f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ LOCAL_SRC_FILES := $(filter-out \
## READ ME: ########################################################
LOCAL_SRC_FILES += \
	core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
	core/java/android/accessibilityservice/IEventListener.aidl \
	core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
	core/java/android/accounts/IAccountManager.aidl \
	core/java/android/accounts/IAccountManagerResponse.aidl \
	core/java/android/accounts/IAccountAuthenticator.aidl \
+45 −0
Original line number Diff line number Diff line
@@ -534,6 +534,7 @@ package android {
    field public static final int imeSubtypeLocale = 16843500; // 0x10102ec
    field public static final int imeSubtypeMode = 16843501; // 0x10102ed
    field public static final int immersive = 16843456; // 0x10102c0
    field public static final int importantForAccessibility = 16843699; // 0x10103b3
    field public static final int inAnimation = 16843127; // 0x1010177
    field public static final int includeFontPadding = 16843103; // 0x101015f
    field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -1993,11 +1994,23 @@ package android.accessibilityservice {
  public abstract class AccessibilityService extends android.app.Service {
    ctor public AccessibilityService();
    method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
    method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
    method public final android.os.IBinder onBind(android.content.Intent);
    method protected void onGesture(int);
    method public abstract void onInterrupt();
    method protected void onServiceConnected();
    method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
    field public static final int GESTURE_CLOCKWISE_CIRCLE = 9; // 0x9
    field public static final int GESTURE_COUNTER_CLOCKWISE_CIRCLE = 10; // 0xa
    field public static final int GESTURE_SWIPE_DOWN = 2; // 0x2
    field public static final int GESTURE_SWIPE_DOWN_AND_UP = 8; // 0x8
    field public static final int GESTURE_SWIPE_LEFT = 3; // 0x3
    field public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5; // 0x5
    field public static final int GESTURE_SWIPE_RIGHT = 4; // 0x4
    field public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6; // 0x6
    field public static final int GESTURE_SWIPE_UP = 1; // 0x1
    field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7
    field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
    field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
  }
@@ -2022,6 +2035,7 @@ package android.accessibilityservice {
    field public static final int FEEDBACK_HAPTIC = 2; // 0x2
    field public static final int FEEDBACK_SPOKEN = 1; // 0x1
    field public static final int FEEDBACK_VISUAL = 8; // 0x8
    field public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
    field public int eventTypes;
    field public int feedbackType;
    field public int flags;
@@ -23327,6 +23341,7 @@ package android.view {
    ctor public View(android.content.Context);
    ctor public View(android.content.Context, android.util.AttributeSet);
    ctor public View(android.content.Context, android.util.AttributeSet, int);
    method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
    method public void addFocusables(java.util.ArrayList<android.view.View>, int);
    method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
    method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
@@ -23429,6 +23444,7 @@ package android.view {
    method public int getHorizontalFadingEdgeLength();
    method protected int getHorizontalScrollbarHeight();
    method public int getId();
    method public int getImportantForAccessibility();
    method public boolean getKeepScreenOn();
    method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
    method public int getLayerType();
@@ -23462,6 +23478,7 @@ package android.view {
    method public int getPaddingStart();
    method public int getPaddingTop();
    method public final android.view.ViewParent getParent();
    method public android.view.ViewParent getParentForAccessibility();
    method public float getPivotX();
    method public float getPivotY();
    method public int getResolvedLayoutDirection();
@@ -23617,6 +23634,7 @@ package android.view {
    method public void onWindowSystemUiVisibilityChanged(int);
    method protected void onWindowVisibilityChanged(int);
    method protected boolean overScrollBy(int, int, int, int, int, int, int, int, boolean);
    method public boolean performAccessibilityAction(int);
    method public boolean performClick();
    method public boolean performHapticFeedback(int);
    method public boolean performHapticFeedback(int, int);
@@ -23688,6 +23706,7 @@ package android.view {
    method public void setHorizontalScrollBarEnabled(boolean);
    method public void setHovered(boolean);
    method public void setId(int);
    method public void setImportantForAccessibility(int);
    method public void setKeepScreenOn(boolean);
    method public void setLayerType(int, android.graphics.Paint);
    method public void setLayoutDirection(int);
@@ -23762,6 +23781,14 @@ package android.view {
    method protected boolean verifyDrawable(android.graphics.drawable.Drawable);
    method public boolean willNotCacheDrawing();
    method public boolean willNotDraw();
    field public static final int ACCESSIBILITY_FOCUS_BACKWARD = 4097; // 0x1001
    field public static final int ACCESSIBILITY_FOCUS_DOWN = 4226; // 0x1082
    field public static final int ACCESSIBILITY_FOCUS_FORWARD = 4098; // 0x1002
    field public static final int ACCESSIBILITY_FOCUS_IN = 4100; // 0x1004
    field public static final int ACCESSIBILITY_FOCUS_LEFT = 4113; // 0x1011
    field public static final int ACCESSIBILITY_FOCUS_OUT = 4104; // 0x1008
    field public static final int ACCESSIBILITY_FOCUS_RIGHT = 4162; // 0x1042
    field public static final int ACCESSIBILITY_FOCUS_UP = 4129; // 0x1021
    field public static final android.util.Property ALPHA;
    field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
    field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
@@ -23783,6 +23810,7 @@ package android.view {
    field protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
    field protected static final int[] FOCUSED_STATE_SET;
    field protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET;
    field public static final int FOCUS_ACCESSIBILITY = 4096; // 0x1000
    field public static final int FOCUS_BACKWARD = 1; // 0x1
    field public static final int FOCUS_DOWN = 130; // 0x82
    field public static final int FOCUS_FORWARD = 2; // 0x2
@@ -23791,6 +23819,9 @@ package android.view {
    field public static final int FOCUS_UP = 33; // 0x21
    field public static final int GONE = 8; // 0x8
    field public static final int HAPTIC_FEEDBACK_ENABLED = 268435456; // 0x10000000
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
    field public static final int INVISIBLE = 4; // 0x4
    field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
    field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -24215,6 +24246,7 @@ package android.view {
    method public abstract void focusableViewAvailable(android.view.View);
    method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
    method public abstract android.view.ViewParent getParent();
    method public abstract android.view.ViewParent getParentForAccessibility();
    method public abstract void invalidateChild(android.view.View, android.graphics.Rect);
    method public abstract android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
    method public abstract boolean isLayoutRequested();
@@ -24616,6 +24648,8 @@ package android.view.accessibility {
    field public static final int TYPE_NOTIFICATION_STATE_CHANGED = 64; // 0x40
    field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
    field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
    field public static final int TYPE_VIEW_CLICKED = 1; // 0x1
    field public static final int TYPE_VIEW_FOCUSED = 8; // 0x8
    field public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
@@ -24656,6 +24690,8 @@ package android.view.accessibility {
    method public void addChild(android.view.View, int);
    method public int describeContents();
    method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String);
    method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
    method public android.view.accessibility.AccessibilityNodeInfo focusSearch(int);
    method public int getActions();
    method public void getBoundsInParent(android.graphics.Rect);
    method public void getBoundsInScreen(android.graphics.Rect);
@@ -24667,6 +24703,7 @@ package android.view.accessibility {
    method public android.view.accessibility.AccessibilityNodeInfo getParent();
    method public java.lang.CharSequence getText();
    method public int getWindowId();
    method public boolean isAccessibilityFocused();
    method public boolean isCheckable();
    method public boolean isChecked();
    method public boolean isClickable();
@@ -24683,6 +24720,7 @@ package android.view.accessibility {
    method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.accessibility.AccessibilityNodeInfo);
    method public boolean performAction(int);
    method public void recycle();
    method public void setAccessibilityFocused(boolean);
    method public void setBoundsInParent(android.graphics.Rect);
    method public void setBoundsInScreen(android.graphics.Rect);
    method public void setCheckable(boolean);
@@ -24704,16 +24742,23 @@ package android.view.accessibility {
    method public void setSource(android.view.View, int);
    method public void setText(java.lang.CharSequence);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final int ACTION_ACCESSIBILITY_FOCUS = 16; // 0x10
    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 32; // 0x20
    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
    field public static final int ACTION_CLICK = 64; // 0x40
    field public static final int ACTION_FOCUS = 1; // 0x1
    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
    field public static final int FOCUS_INPUT = 1; // 0x1
  }
  public abstract class AccessibilityNodeProvider {
    ctor public AccessibilityNodeProvider();
    method public android.view.accessibility.AccessibilityNodeInfo accessibilityFocusSearch(int, int);
    method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
    method public android.view.accessibility.AccessibilityNodeInfo findAccessibilitiyFocus(int);
    method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
    method public boolean performAccessibilityAction(int, int);
  }
+195 −4
Original line number Diff line number Diff line
@@ -19,17 +19,22 @@ package android.accessibilityservice;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.LocaleUtil;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;

import com.android.internal.os.HandlerCaller;

import java.util.Locale;

/**
 * An accessibility service runs in the background and receives callbacks by the system
 * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
@@ -202,12 +207,65 @@ import com.android.internal.os.HandlerCaller;
 * @see android.view.accessibility.AccessibilityManager
 */
public abstract class AccessibilityService extends Service {

    /**
     * The user has performed a swipe up gesture on the touch screen.
     */
    public static final int GESTURE_SWIPE_UP = 1;

    /**
     * The user has performed a swipe down gesture on the touch screen.
     */
    public static final int GESTURE_SWIPE_DOWN = 2;

    /**
     * The user has performed a swipe left gesture on the touch screen.
     */
    public static final int GESTURE_SWIPE_LEFT = 3;

    /**
     * The user has performed a swipe right gesture on the touch screen.
     */
    public static final int GESTURE_SWIPE_RIGHT = 4;

    /**
     * The user has performed a swipe left and right gesture on the touch screen.
     */
    public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5;

    /**
     * The user has performed a swipe right and left gesture on the touch screen.
     */
    public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6;

    /**
     * The user has performed a swipe up and down gesture on the touch screen.
     */
    public static final int GESTURE_SWIPE_UP_AND_DOWN = 7;

    /**
     * The user has performed a swipe down and up gesture on the touch screen.
     */
    public static final int GESTURE_SWIPE_DOWN_AND_UP = 8;

    /**
     * The user has performed a clockwise circle gesture on the touch screen.
     */
    public static final int GESTURE_CLOCKWISE_CIRCLE = 9;

    /**
     * The user has performed a counter clockwise circle gesture on the touch screen.
     */
    public static final int GESTURE_COUNTER_CLOCKWISE_CIRCLE = 10;

    /**
     * The {@link Intent} that must be declared as handled by the service.
     */
    public static final String SERVICE_INTERFACE =
        "android.accessibilityservice.AccessibilityService";

    private static final int UNDEFINED = -1;

    /**
     * Name under which an AccessibilityService component publishes information
     * about itself. This meta-data must reference an XML resource containing an
@@ -233,12 +291,15 @@ public abstract class AccessibilityService extends Service {
        public void onInterrupt();
        public void onServiceConnected();
        public void onSetConnectionId(int connectionId);
        public void onGesture(int gestureId);
    }

    private int mConnectionId;

    private AccessibilityServiceInfo mInfo;

    private int mLayoutDirection;

    /**
     * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
     *
@@ -263,6 +324,106 @@ public abstract class AccessibilityService extends Service {

    }

    /**
     * Called by the system when the user performs a specific gesture on the
     * touch screen.
     *
     * @param gestureId The unique id of the performed gesture.
     *
     * @see #GESTURE_SWIPE_UP
     * @see #GESTURE_SWIPE_DOWN
     * @see #GESTURE_SWIPE_LEFT
     * @see #GESTURE_SWIPE_RIGHT
     * @see #GESTURE_SWIPE_UP_AND_DOWN
     * @see #GESTURE_SWIPE_DOWN_AND_UP
     * @see #GESTURE_SWIPE_LEFT_AND_RIGHT
     * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
     * @see #GESTURE_CLOCKWISE_CIRCLE
     * @see #GESTURE_COUNTER_CLOCKWISE_CIRCLE
     */
    protected void onGesture(int gestureId) {
        // TODO: Describe the default gesture processing in the javaDoc once it is finalized.

        // Cache the id to avoid locking
        final int connectionId = mConnectionId;
        if (connectionId == UNDEFINED) {
            throw new IllegalStateException("AccessibilityService not connected."
                    + " Did you receive a call of onServiceConnected()?");
        }
        AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
                .findAccessibilityNodeInfoByAccessibilityId(connectionId,
                        AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
                        AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
        if (root == null) {
            return;
        }
        AccessibilityNodeInfo current = root.findFocus(View.FOCUS_ACCESSIBILITY);
        if (current == null) {
            current = root;
        }
        AccessibilityNodeInfo next = null;
        switch (gestureId) {
            case GESTURE_SWIPE_UP: {
                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_OUT);
            } break;
            case GESTURE_SWIPE_DOWN: {
                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_IN);
            } break;
            case GESTURE_SWIPE_LEFT: {
                if (mLayoutDirection == View.LAYOUT_DIRECTION_LTR) {
                    next = current.focusSearch(View.ACCESSIBILITY_FOCUS_BACKWARD);
                } else { // LAYOUT_DIRECTION_RTL
                    next = current.focusSearch(View.ACCESSIBILITY_FOCUS_FORWARD);
                }
            } break;
            case GESTURE_SWIPE_RIGHT: {
                if (mLayoutDirection == View.LAYOUT_DIRECTION_LTR) {
                    next = current.focusSearch(View.ACCESSIBILITY_FOCUS_FORWARD);
                } else { // LAYOUT_DIRECTION_RTL
                    next = current.focusSearch(View.ACCESSIBILITY_FOCUS_BACKWARD);
                }
            } break;
            case GESTURE_SWIPE_UP_AND_DOWN: {
                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_UP);
            } break;
            case GESTURE_SWIPE_DOWN_AND_UP: {
                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_DOWN);
            } break;
            case GESTURE_SWIPE_LEFT_AND_RIGHT: {
                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_LEFT);
            } break;
            case GESTURE_SWIPE_RIGHT_AND_LEFT: {
                next = current.focusSearch(View.ACCESSIBILITY_FOCUS_RIGHT);
            } break;
        }
        if (next != null && !next.equals(current)) {
            next.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
        }
    }

    /**
     * Gets the an {@link AccessibilityServiceInfo} describing this
     * {@link AccessibilityService}. This method is useful if one wants
     * to change some of the dynamically configurable properties at
     * runtime.
     *
     * @return The accessibility service info.
     *
     * @see AccessibilityNodeInfo
     */
    public final AccessibilityServiceInfo getServiceInfo() {
        IAccessibilityServiceConnection connection =
            AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
        if (connection != null) {
            try {
                return connection.getServiceInfo();
            } catch (RemoteException re) {
                Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
            }
        }
        return null;
    }

    /**
     * Sets the {@link AccessibilityServiceInfo} that describes this service.
     * <p>
@@ -287,19 +448,33 @@ public abstract class AccessibilityService extends Service {
        if (mInfo != null && connection != null) {
            try {
                connection.setServiceInfo(mInfo);
                mInfo = null;
                AccessibilityInteractionClient.getInstance().clearCache();
            } catch (RemoteException re) {
                Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
            }
        }
    }

    @Override
    public void onCreate() {
        Locale locale = getResources().getConfiguration().locale;
        mLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {
        super.onConfigurationChanged(configuration);
        mLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(configuration.locale);
    }

    /**
     * Implement to return the implementation of the internal accessibility
     * service interface.
     */
    @Override
    public final IBinder onBind(Intent intent) {
        return new IEventListenerWrapper(this, getMainLooper(), new Callbacks() {
        return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
            @Override
            public void onServiceConnected() {
                AccessibilityService.this.onServiceConnected();
@@ -319,14 +494,19 @@ public abstract class AccessibilityService extends Service {
            public void onSetConnectionId( int connectionId) {
                mConnectionId = connectionId;
            }

            @Override
            public void onGesture(int gestureId) {
                AccessibilityService.this.onGesture(gestureId);
            }
        });
    }

    /**
     * Implements the internal {@link IEventListener} interface to convert
     * Implements the internal {@link IAccessibilityServiceClient} interface to convert
     * incoming calls to it back to calls on an {@link AccessibilityService}.
     */
    static class IEventListenerWrapper extends IEventListener.Stub
    static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
            implements HandlerCaller.Callback {

        static final int NO_ID = -1;
@@ -334,12 +514,14 @@ public abstract class AccessibilityService extends Service {
        private static final int DO_SET_SET_CONNECTION = 10;
        private static final int DO_ON_INTERRUPT = 20;
        private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
        private static final int DO_ON_GESTURE = 40;

        private final HandlerCaller mCaller;

        private final Callbacks mCallback;

        public IEventListenerWrapper(Context context, Looper looper, Callbacks callback) {
        public IAccessibilityServiceClientWrapper(Context context, Looper looper,
                Callbacks callback) {
            mCallback = callback;
            mCaller = new HandlerCaller(context, looper, this);
        }
@@ -360,6 +542,11 @@ public abstract class AccessibilityService extends Service {
            mCaller.sendMessage(message);
        }

        public void onGesture(int gestureId) {
            Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
            mCaller.sendMessage(message);
        }

        public void executeMessage(Message message) {
            switch (message.what) {
                case DO_ON_ACCESSIBILITY_EVENT :
@@ -387,6 +574,10 @@ public abstract class AccessibilityService extends Service {
                        mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID);
                    }
                    return;
                case DO_ON_GESTURE :
                    final int gestureId = message.arg1;
                    mCallback.onGesture(gestureId);
                    return;
                default :
                    Log.w(LOG_TAG, "Unknown message type " + message.what);
            }
+36 −0
Original line number Diff line number Diff line
@@ -25,11 +25,13 @@ import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.util.Xml;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;

import org.xmlpull.v1.XmlPullParser;
@@ -100,6 +102,37 @@ public class AccessibilityServiceInfo implements Parcelable {
     */
    public static final int DEFAULT = 0x0000001;

    /**
     * If this flag is set the system will regard views that are not important
     * for accessibility in addition to the ones that are important for accessibility.
     * That is, views that are marked as not important for accessibility via
     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} and views that are marked as
     * potentially important for accessibility via
     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined
     * that are not important for accessibility, are both reported while querying the
     * window content and also the accessibility service will receive accessibility events
     * from them.
     * <p>
     * <strong>Note:</strong> For accessibility services targeting API version
     * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly
     * set for the system to regard views that are not important for accessibility. For
     * accessibility services targeting API version lower than
     * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are
     * regarded for accessibility purposes.
     * </p>
     * <p>
     * Usually views not important for accessibility are layout managers that do not
     * react to user actions, do not draw any content, and do not have any special
     * semantics in the context of the screen content. For example, a three by three
     * grid can be implemented as three horizontal linear layouts and one vertical,
     * or three vertical linear layouts and one horizontal, or one grid layout, etc.
     * In this context the actual layout mangers used to achieve the grid configuration
     * are not important, rather it is important that there are nine evenly distributed
     * elements.
     * </p>
     */
    public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002;

    /**
     * The event types an {@link AccessibilityService} is interested in.
     * <p>
@@ -165,6 +198,7 @@ public class AccessibilityServiceInfo implements Parcelable {
     *   <strong>Can be dynamically set at runtime.</strong>
     * </p>
     * @see #DEFAULT
     * @see #INCLUDE_NOT_IMPORTANT_VIEWS
     */
    public int flags;

@@ -561,6 +595,8 @@ public class AccessibilityServiceInfo implements Parcelable {
        switch (flag) {
            case DEFAULT:
                return "DEFAULT";
            case INCLUDE_NOT_IMPORTANT_VIEWS:
                return "REGARD_VIEWS_NOT_IMPORTANT_FOR_ACCESSIBILITY";
            default:
                return null;
        }
+4 −2

File changed and moved.

Preview size limit exceeded, changes collapsed.

Loading