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

Commit e30df774 authored by Svetoslav Ganov's avatar Svetoslav Ganov Committed by Android (Google) Code Review
Browse files

Merge "Adding an opt-in mechanism for gesture detection in AccessibilityService."

parents b0668e4d fefd20e9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ LOCAL_SRC_FILES := $(filter-out \
LOCAL_SRC_FILES += \
	core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
	core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
	core/java/android/accessibilityservice/IAccessibilityServiceClientCallback.aidl \
	core/java/android/accounts/IAccountManager.aidl \
	core/java/android/accounts/IAccountManagerResponse.aidl \
	core/java/android/accounts/IAccountAuthenticator.aidl \
+5 −1
Original line number Diff line number Diff line
@@ -294,6 +294,7 @@ package android {
    field public static final int cacheColorHint = 16843009; // 0x1010101
    field public static final int calendarViewShown = 16843596; // 0x101034c
    field public static final int calendarViewStyle = 16843613; // 0x101035d
    field public static final int canHandleGestures = 16843699; // 0x10103b3
    field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
    field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
    field public static final deprecated int capitalize = 16843113; // 0x1010169
@@ -1997,7 +1998,7 @@ package android.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 protected boolean onGesture(int);
    method public abstract void onInterrupt();
    method protected void onServiceConnected();
    method public final boolean performGlobalAction(int);
@@ -2020,6 +2021,8 @@ package android.accessibilityservice {
    field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7
    field public static final int GESTURE_SWIPE_UP_AND_LEFT = 15; // 0xf
    field public static final int GESTURE_SWIPE_UP_AND_RIGHT = 16; // 0x10
    field public static final int GESTURE_TWO_FINGER_LONG_PRESS = 20; // 0x14
    field public static final int GESTURE_TWO_FINGER_TAP = 19; // 0x13
    field public static final int GLOBAL_ACTION_BACK = 1; // 0x1
    field public static final int GLOBAL_ACTION_HOME = 2; // 0x2
    field public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; // 0x4
@@ -2033,6 +2036,7 @@ package android.accessibilityservice {
    method public int describeContents();
    method public static java.lang.String feedbackTypeToString(int);
    method public static java.lang.String flagToString(int);
    method public boolean getCanHandleGestures();
    method public boolean getCanRetrieveWindowContent();
    method public deprecated java.lang.String getDescription();
    method public java.lang.String getId();
+53 −109
Original line number Diff line number Diff line
@@ -19,22 +19,17 @@ 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
@@ -298,6 +293,16 @@ public abstract class AccessibilityService extends Service {
     */
    public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 18;

    /**
     * The user has performed a two finger tap gesture on the touch screen.
     */
    public static final int GESTURE_TWO_FINGER_TAP = 19;

    /**
     * The user has performed a two finger long press gesture on the touch screen.
     */
    public static final int GESTURE_TWO_FINGER_LONG_PRESS = 20;

    /**
     * The {@link Intent} that must be declared as handled by the service.
     */
@@ -342,8 +347,6 @@ public abstract class AccessibilityService extends Service {
     */
    public static final int GLOBAL_ACTION_NOTIFICATIONS = 4;

    private static final int UNDEFINED = -1;

    private static final String LOG_TAG = "AccessibilityService";

    interface Callbacks {
@@ -351,15 +354,13 @@ public abstract class AccessibilityService extends Service {
        public void onInterrupt();
        public void onServiceConnected();
        public void onSetConnectionId(int connectionId);
        public void onGesture(int gestureId);
        public boolean onGesture(int gestureId);
    }

    private int mConnectionId;

    private AccessibilityServiceInfo mInfo;

    private int mLayoutDirection;

    /**
     * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
     *
@@ -386,95 +387,43 @@ public abstract class AccessibilityService extends Service {

    /**
     * Called by the system when the user performs a specific gesture on the
     * touch screen.
     * touch screen. If the gesture is not handled in this callback the system
     * may provide default handing. Therefore, one should return true from this
     * function if overriding of default behavior is desired.
     *
     * <strong>Note:</strong> To receive gestures an accessibility service
     *         must declare that it can handle such by specifying the
     *         <code>&lt;{@link android.R.styleable#AccessibilityService_canHandleGestures
     *         canHandleGestures}&gt;</code> attribute.
     *
     * @param gestureId The unique id of the performed gesture.
     *
     * @return Whether the gesture was handled.
     *
     * @see #GESTURE_SWIPE_UP
     * @see #GESTURE_SWIPE_DOWN
     * @see #GESTURE_SWIPE_LEFT
     * @see #GESTURE_SWIPE_RIGHT
     * @see #GESTURE_SWIPE_UP_AND_LEFT
     * @see #GESTURE_SWIPE_UP_AND_DOWN
     * @see #GESTURE_SWIPE_UP_AND_RIGHT
     * @see #GESTURE_SWIPE_DOWN
     * @see #GESTURE_SWIPE_DOWN_AND_LEFT
     * @see #GESTURE_SWIPE_DOWN_AND_UP
     * @see #GESTURE_SWIPE_DOWN_AND_RIGHT
     * @see #GESTURE_SWIPE_LEFT
     * @see #GESTURE_SWIPE_LEFT_AND_UP
     * @see #GESTURE_SWIPE_LEFT_AND_RIGHT
     * @see #GESTURE_SWIPE_LEFT_AND_DOWN
     * @see #GESTURE_SWIPE_RIGHT
     * @see #GESTURE_SWIPE_RIGHT_AND_UP
     * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
     * @see #GESTURE_SWIPE_RIGHT_AND_DOWN
     * @see #GESTURE_CLOCKWISE_CIRCLE
     * @see #GESTURE_COUNTER_CLOCKWISE_CIRCLE
     * @see #GESTURE_TWO_FINGER_TAP
     * @see #GESTURE_TWO_FINGER_LONG_PRESS
     */
    protected void onGesture(int gestureId) {
    protected boolean onGesture(int gestureId) {
        // TODO: Describe the default gesture processing in the javaDoc once it is finalized.

        // Global actions.
        switch (gestureId) {
            case GESTURE_SWIPE_DOWN_AND_LEFT: {
                performGlobalAction(GLOBAL_ACTION_BACK);
            } return;
            case GESTURE_SWIPE_DOWN_AND_RIGHT: {
                performGlobalAction(GLOBAL_ACTION_HOME);
            } return;
            case GESTURE_SWIPE_UP_AND_LEFT: {
                performGlobalAction(GLOBAL_ACTION_RECENTS);
            } return;
            case GESTURE_SWIPE_UP_AND_RIGHT: {
                performGlobalAction(GLOBAL_ACTION_NOTIFICATIONS);
            } return;
        }

        // 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 = getRootInActiveWindow();
        if (root == null) {
            return;
        }

        AccessibilityNodeInfo current = root.findFocus(AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
        if (current == null) {
            current = root;
        }

        // Local actions.
        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);
        }
        return false;
    }

    /**
@@ -484,10 +433,7 @@ public abstract class AccessibilityService extends Service {
     * @return The root node if this service can retrieve window content.
     */
    public AccessibilityNodeInfo getRootInActiveWindow() {
        return AccessibilityInteractionClient.getInstance()
            .findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
                AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
                AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
        return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId);
    }

    /**
@@ -509,7 +455,7 @@ public abstract class AccessibilityService extends Service {
            AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
        if (connection != null) {
            try {
                return connection.perfromGlobalAction(action);
                return connection.performGlobalAction(action);
            } catch (RemoteException re) {
                Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
            }
@@ -572,18 +518,6 @@ public abstract class AccessibilityService extends Service {
        }
    }

    @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.
@@ -612,8 +546,8 @@ public abstract class AccessibilityService extends Service {
            }

            @Override
            public void onGesture(int gestureId) {
                AccessibilityService.this.onGesture(gestureId);
            public boolean onGesture(int gestureId) {
                return AccessibilityService.this.onGesture(gestureId);
            }
        });
    }
@@ -658,8 +592,10 @@ public abstract class AccessibilityService extends Service {
            mCaller.sendMessage(message);
        }

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

@@ -692,7 +628,15 @@ public abstract class AccessibilityService extends Service {
                    return;
                case DO_ON_GESTURE :
                    final int gestureId = message.arg1;
                    mCallback.onGesture(gestureId);
                    final int interactionId = message.arg2;
                    IAccessibilityServiceClientCallback callback =
                        (IAccessibilityServiceClientCallback) message.obj;
                    final boolean handled = mCallback.onGesture(gestureId);
                    try {
                        callback.setGestureResult(gestureId, handled, interactionId);
                    } catch (RemoteException re) {
                        Log.e(LOG_TAG, "Error calling back with the gesture resut.", re);
                    }
                    return;
                default :
                    Log.w(LOG_TAG, "Unknown message type " + message.what);
+20 −1
Original line number Diff line number Diff line
@@ -223,6 +223,11 @@ public class AccessibilityServiceInfo implements Parcelable {
     */
    private boolean mCanRetrieveWindowContent;

    /**
     * Flag whether this accessibility service can handle gestures.
     */
    private boolean mCanHandleGestures;

    /**
     * Resource id of the description of the accessibility service.
     */
@@ -303,6 +308,8 @@ public class AccessibilityServiceInfo implements Parcelable {
            mCanRetrieveWindowContent = asAttributes.getBoolean(
                    com.android.internal.R.styleable.AccessibilityService_canRetrieveWindowContent,
                    false);
            mCanHandleGestures = asAttributes.getBoolean(
                    com.android.internal.R.styleable.AccessibilityService_canHandleGestures, false);
            TypedValue peekedValue = asAttributes.peekValue(
                    com.android.internal.R.styleable.AccessibilityService_description);
            if (peekedValue != null) {
@@ -378,12 +385,24 @@ public class AccessibilityServiceInfo implements Parcelable {
     *    <strong>Statically set from
     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
     * </p>
     * @return True window content can be retrieved.
     * @return True if window content can be retrieved.
     */
    public boolean getCanRetrieveWindowContent() {
        return mCanRetrieveWindowContent;
    }

    /**
     * Whether this service can handle gestures.
     * <p>
     *    <strong>Statically set from
     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
     * </p>
     * @return True if the service can handle gestures.
     */
    public boolean getCanHandleGestures() {
        return mCanHandleGestures;
    }

    /**
     * Gets the non-localized description of the accessibility service.
     * <p>
+2 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.accessibilityservice;

import android.accessibilityservice.IAccessibilityServiceClientCallback;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.view.accessibility.AccessibilityEvent;

@@ -32,5 +33,5 @@ import android.view.accessibility.AccessibilityEvent;

    void onInterrupt();

    void onGesture(int gestureId);
    void onGesture(int gesture, in IAccessibilityServiceClientCallback callback, int interactionId);
}
Loading