Skip to content

Commit e30df77

Browse files
sganovAndroid (Google) Code Review
authored andcommitted
Merge "Adding an opt-in mechanism for gesture detection in AccessibilityService."
2 parents b0668e4 + fefd20e commit e30df77

File tree

13 files changed

+402
-133
lines changed

13 files changed

+402
-133
lines changed

Android.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ LOCAL_SRC_FILES := $(filter-out \
6161
LOCAL_SRC_FILES += \
6262
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
6363
core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
64+
core/java/android/accessibilityservice/IAccessibilityServiceClientCallback.aidl \
6465
core/java/android/accounts/IAccountManager.aidl \
6566
core/java/android/accounts/IAccountManagerResponse.aidl \
6667
core/java/android/accounts/IAccountAuthenticator.aidl \

api/current.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ package android {
294294
field public static final int cacheColorHint = 16843009; // 0x1010101
295295
field public static final int calendarViewShown = 16843596; // 0x101034c
296296
field public static final int calendarViewStyle = 16843613; // 0x101035d
297+
field public static final int canHandleGestures = 16843699; // 0x10103b3
297298
field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
298299
field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
299300
field public static final deprecated int capitalize = 16843113; // 0x1010169
@@ -1997,7 +1998,7 @@ package android.accessibilityservice {
19971998
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
19981999
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
19992000
method public final android.os.IBinder onBind(android.content.Intent);
2000-
method protected void onGesture(int);
2001+
method protected boolean onGesture(int);
20012002
method public abstract void onInterrupt();
20022003
method protected void onServiceConnected();
20032004
method public final boolean performGlobalAction(int);
@@ -2020,6 +2021,8 @@ package android.accessibilityservice {
20202021
field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7
20212022
field public static final int GESTURE_SWIPE_UP_AND_LEFT = 15; // 0xf
20222023
field public static final int GESTURE_SWIPE_UP_AND_RIGHT = 16; // 0x10
2024+
field public static final int GESTURE_TWO_FINGER_LONG_PRESS = 20; // 0x14
2025+
field public static final int GESTURE_TWO_FINGER_TAP = 19; // 0x13
20232026
field public static final int GLOBAL_ACTION_BACK = 1; // 0x1
20242027
field public static final int GLOBAL_ACTION_HOME = 2; // 0x2
20252028
field public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; // 0x4
@@ -2033,6 +2036,7 @@ package android.accessibilityservice {
20332036
method public int describeContents();
20342037
method public static java.lang.String feedbackTypeToString(int);
20352038
method public static java.lang.String flagToString(int);
2039+
method public boolean getCanHandleGestures();
20362040
method public boolean getCanRetrieveWindowContent();
20372041
method public deprecated java.lang.String getDescription();
20382042
method public java.lang.String getId();

core/java/android/accessibilityservice/AccessibilityService.java

Lines changed: 53 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,17 @@
1919
import android.app.Service;
2020
import android.content.Context;
2121
import android.content.Intent;
22-
import android.content.res.Configuration;
2322
import android.os.IBinder;
2423
import android.os.Looper;
2524
import android.os.Message;
2625
import android.os.RemoteException;
27-
import android.util.LocaleUtil;
2826
import android.util.Log;
29-
import android.view.View;
3027
import android.view.accessibility.AccessibilityEvent;
3128
import android.view.accessibility.AccessibilityInteractionClient;
3229
import android.view.accessibility.AccessibilityNodeInfo;
3330

3431
import com.android.internal.os.HandlerCaller;
3532

36-
import java.util.Locale;
37-
3833
/**
3934
* An accessibility service runs in the background and receives callbacks by the system
4035
* when {@link AccessibilityEvent}s are fired. Such events denote some state transition
@@ -298,6 +293,16 @@ public abstract class AccessibilityService extends Service {
298293
*/
299294
public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 18;
300295

296+
/**
297+
* The user has performed a two finger tap gesture on the touch screen.
298+
*/
299+
public static final int GESTURE_TWO_FINGER_TAP = 19;
300+
301+
/**
302+
* The user has performed a two finger long press gesture on the touch screen.
303+
*/
304+
public static final int GESTURE_TWO_FINGER_LONG_PRESS = 20;
305+
301306
/**
302307
* The {@link Intent} that must be declared as handled by the service.
303308
*/
@@ -342,24 +347,20 @@ public abstract class AccessibilityService extends Service {
342347
*/
343348
public static final int GLOBAL_ACTION_NOTIFICATIONS = 4;
344349

345-
private static final int UNDEFINED = -1;
346-
347350
private static final String LOG_TAG = "AccessibilityService";
348351

349352
interface Callbacks {
350353
public void onAccessibilityEvent(AccessibilityEvent event);
351354
public void onInterrupt();
352355
public void onServiceConnected();
353356
public void onSetConnectionId(int connectionId);
354-
public void onGesture(int gestureId);
357+
public boolean onGesture(int gestureId);
355358
}
356359

357360
private int mConnectionId;
358361

359362
private AccessibilityServiceInfo mInfo;
360363

361-
private int mLayoutDirection;
362-
363364
/**
364365
* Callback for {@link android.view.accessibility.AccessibilityEvent}s.
365366
*
@@ -386,95 +387,43 @@ protected void onServiceConnected() {
386387

387388
/**
388389
* Called by the system when the user performs a specific gesture on the
389-
* touch screen.
390+
* touch screen. If the gesture is not handled in this callback the system
391+
* may provide default handing. Therefore, one should return true from this
392+
* function if overriding of default behavior is desired.
393+
*
394+
* <strong>Note:</strong> To receive gestures an accessibility service
395+
* must declare that it can handle such by specifying the
396+
* <code>&lt;{@link android.R.styleable#AccessibilityService_canHandleGestures
397+
* canHandleGestures}&gt;</code> attribute.
390398
*
391399
* @param gestureId The unique id of the performed gesture.
392400
*
401+
* @return Whether the gesture was handled.
402+
*
393403
* @see #GESTURE_SWIPE_UP
394-
* @see #GESTURE_SWIPE_DOWN
395-
* @see #GESTURE_SWIPE_LEFT
396-
* @see #GESTURE_SWIPE_RIGHT
404+
* @see #GESTURE_SWIPE_UP_AND_LEFT
397405
* @see #GESTURE_SWIPE_UP_AND_DOWN
406+
* @see #GESTURE_SWIPE_UP_AND_RIGHT
407+
* @see #GESTURE_SWIPE_DOWN
408+
* @see #GESTURE_SWIPE_DOWN_AND_LEFT
398409
* @see #GESTURE_SWIPE_DOWN_AND_UP
410+
* @see #GESTURE_SWIPE_DOWN_AND_RIGHT
411+
* @see #GESTURE_SWIPE_LEFT
412+
* @see #GESTURE_SWIPE_LEFT_AND_UP
399413
* @see #GESTURE_SWIPE_LEFT_AND_RIGHT
414+
* @see #GESTURE_SWIPE_LEFT_AND_DOWN
415+
* @see #GESTURE_SWIPE_RIGHT
416+
* @see #GESTURE_SWIPE_RIGHT_AND_UP
400417
* @see #GESTURE_SWIPE_RIGHT_AND_LEFT
418+
* @see #GESTURE_SWIPE_RIGHT_AND_DOWN
401419
* @see #GESTURE_CLOCKWISE_CIRCLE
402420
* @see #GESTURE_COUNTER_CLOCKWISE_CIRCLE
421+
* @see #GESTURE_TWO_FINGER_TAP
422+
* @see #GESTURE_TWO_FINGER_LONG_PRESS
403423
*/
404-
protected void onGesture(int gestureId) {
424+
protected boolean onGesture(int gestureId) {
405425
// TODO: Describe the default gesture processing in the javaDoc once it is finalized.
406-
407-
// Global actions.
408-
switch (gestureId) {
409-
case GESTURE_SWIPE_DOWN_AND_LEFT: {
410-
performGlobalAction(GLOBAL_ACTION_BACK);
411-
} return;
412-
case GESTURE_SWIPE_DOWN_AND_RIGHT: {
413-
performGlobalAction(GLOBAL_ACTION_HOME);
414-
} return;
415-
case GESTURE_SWIPE_UP_AND_LEFT: {
416-
performGlobalAction(GLOBAL_ACTION_RECENTS);
417-
} return;
418-
case GESTURE_SWIPE_UP_AND_RIGHT: {
419-
performGlobalAction(GLOBAL_ACTION_NOTIFICATIONS);
420-
} return;
421-
}
422-
423-
// Cache the id to avoid locking
424-
final int connectionId = mConnectionId;
425-
if (connectionId == UNDEFINED) {
426-
throw new IllegalStateException("AccessibilityService not connected."
427-
+ " Did you receive a call of onServiceConnected()?");
428-
}
429-
AccessibilityNodeInfo root = getRootInActiveWindow();
430-
if (root == null) {
431-
return;
432-
}
433-
434-
AccessibilityNodeInfo current = root.findFocus(AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
435-
if (current == null) {
436-
current = root;
437-
}
438-
439-
// Local actions.
440-
AccessibilityNodeInfo next = null;
441-
switch (gestureId) {
442-
case GESTURE_SWIPE_UP: {
443-
next = current.focusSearch(View.ACCESSIBILITY_FOCUS_OUT);
444-
} break;
445-
case GESTURE_SWIPE_DOWN: {
446-
next = current.focusSearch(View.ACCESSIBILITY_FOCUS_IN);
447-
} break;
448-
case GESTURE_SWIPE_LEFT: {
449-
if (mLayoutDirection == View.LAYOUT_DIRECTION_LTR) {
450-
next = current.focusSearch(View.ACCESSIBILITY_FOCUS_BACKWARD);
451-
} else { // LAYOUT_DIRECTION_RTL
452-
next = current.focusSearch(View.ACCESSIBILITY_FOCUS_FORWARD);
453-
}
454-
} break;
455-
case GESTURE_SWIPE_RIGHT: {
456-
if (mLayoutDirection == View.LAYOUT_DIRECTION_LTR) {
457-
next = current.focusSearch(View.ACCESSIBILITY_FOCUS_FORWARD);
458-
} else { // LAYOUT_DIRECTION_RTL
459-
next = current.focusSearch(View.ACCESSIBILITY_FOCUS_BACKWARD);
460-
}
461-
} break;
462-
case GESTURE_SWIPE_UP_AND_DOWN: {
463-
next = current.focusSearch(View.ACCESSIBILITY_FOCUS_UP);
464-
} break;
465-
case GESTURE_SWIPE_DOWN_AND_UP: {
466-
next = current.focusSearch(View.ACCESSIBILITY_FOCUS_DOWN);
467-
} break;
468-
case GESTURE_SWIPE_LEFT_AND_RIGHT: {
469-
next = current.focusSearch(View.ACCESSIBILITY_FOCUS_LEFT);
470-
} break;
471-
case GESTURE_SWIPE_RIGHT_AND_LEFT: {
472-
next = current.focusSearch(View.ACCESSIBILITY_FOCUS_RIGHT);
473-
} break;
474-
}
475-
if (next != null && !next.equals(current)) {
476-
next.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
477-
}
426+
return false;
478427
}
479428

480429
/**
@@ -484,10 +433,7 @@ protected void onGesture(int gestureId) {
484433
* @return The root node if this service can retrieve window content.
485434
*/
486435
public AccessibilityNodeInfo getRootInActiveWindow() {
487-
return AccessibilityInteractionClient.getInstance()
488-
.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
489-
AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
490-
AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
436+
return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId);
491437
}
492438

493439
/**
@@ -509,7 +455,7 @@ public final boolean performGlobalAction(int action) {
509455
AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
510456
if (connection != null) {
511457
try {
512-
return connection.perfromGlobalAction(action);
458+
return connection.performGlobalAction(action);
513459
} catch (RemoteException re) {
514460
Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
515461
}
@@ -572,18 +518,6 @@ private void sendServiceInfo() {
572518
}
573519
}
574520

575-
@Override
576-
public void onCreate() {
577-
Locale locale = getResources().getConfiguration().locale;
578-
mLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
579-
}
580-
581-
@Override
582-
public void onConfigurationChanged(Configuration configuration) {
583-
super.onConfigurationChanged(configuration);
584-
mLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(configuration.locale);
585-
}
586-
587521
/**
588522
* Implement to return the implementation of the internal accessibility
589523
* service interface.
@@ -612,8 +546,8 @@ public void onSetConnectionId( int connectionId) {
612546
}
613547

614548
@Override
615-
public void onGesture(int gestureId) {
616-
AccessibilityService.this.onGesture(gestureId);
549+
public boolean onGesture(int gestureId) {
550+
return AccessibilityService.this.onGesture(gestureId);
617551
}
618552
});
619553
}
@@ -658,8 +592,10 @@ public void onAccessibilityEvent(AccessibilityEvent event) {
658592
mCaller.sendMessage(message);
659593
}
660594

661-
public void onGesture(int gestureId) {
662-
Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
595+
public void onGesture(int gestureId, IAccessibilityServiceClientCallback callback,
596+
int interactionId) {
597+
Message message = mCaller.obtainMessageIIO(DO_ON_GESTURE, gestureId, interactionId,
598+
callback);
663599
mCaller.sendMessage(message);
664600
}
665601

@@ -692,7 +628,15 @@ public void executeMessage(Message message) {
692628
return;
693629
case DO_ON_GESTURE :
694630
final int gestureId = message.arg1;
695-
mCallback.onGesture(gestureId);
631+
final int interactionId = message.arg2;
632+
IAccessibilityServiceClientCallback callback =
633+
(IAccessibilityServiceClientCallback) message.obj;
634+
final boolean handled = mCallback.onGesture(gestureId);
635+
try {
636+
callback.setGestureResult(gestureId, handled, interactionId);
637+
} catch (RemoteException re) {
638+
Log.e(LOG_TAG, "Error calling back with the gesture resut.", re);
639+
}
696640
return;
697641
default :
698642
Log.w(LOG_TAG, "Unknown message type " + message.what);

core/java/android/accessibilityservice/AccessibilityServiceInfo.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ public class AccessibilityServiceInfo implements Parcelable {
223223
*/
224224
private boolean mCanRetrieveWindowContent;
225225

226+
/**
227+
* Flag whether this accessibility service can handle gestures.
228+
*/
229+
private boolean mCanHandleGestures;
230+
226231
/**
227232
* Resource id of the description of the accessibility service.
228233
*/
@@ -303,6 +308,8 @@ public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)
303308
mCanRetrieveWindowContent = asAttributes.getBoolean(
304309
com.android.internal.R.styleable.AccessibilityService_canRetrieveWindowContent,
305310
false);
311+
mCanHandleGestures = asAttributes.getBoolean(
312+
com.android.internal.R.styleable.AccessibilityService_canHandleGestures, false);
306313
TypedValue peekedValue = asAttributes.peekValue(
307314
com.android.internal.R.styleable.AccessibilityService_description);
308315
if (peekedValue != null) {
@@ -378,12 +385,24 @@ public String getSettingsActivityName() {
378385
* <strong>Statically set from
379386
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
380387
* </p>
381-
* @return True window content can be retrieved.
388+
* @return True if window content can be retrieved.
382389
*/
383390
public boolean getCanRetrieveWindowContent() {
384391
return mCanRetrieveWindowContent;
385392
}
386393

394+
/**
395+
* Whether this service can handle gestures.
396+
* <p>
397+
* <strong>Statically set from
398+
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
399+
* </p>
400+
* @return True if the service can handle gestures.
401+
*/
402+
public boolean getCanHandleGestures() {
403+
return mCanHandleGestures;
404+
}
405+
387406
/**
388407
* Gets the non-localized description of the accessibility service.
389408
* <p>

core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package android.accessibilityservice;
1818

19+
import android.accessibilityservice.IAccessibilityServiceClientCallback;
1920
import android.accessibilityservice.IAccessibilityServiceConnection;
2021
import android.view.accessibility.AccessibilityEvent;
2122

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

3334
void onInterrupt();
3435

35-
void onGesture(int gestureId);
36+
void onGesture(int gesture, in IAccessibilityServiceClientCallback callback, int interactionId);
3637
}

0 commit comments

Comments
 (0)