Merge "Adding system support for a single accessibility focus."
This commit is contained in:
@@ -2456,6 +2456,7 @@ package android.accessibilityservice {
|
|||||||
|
|
||||||
public abstract class AccessibilityService extends android.app.Service {
|
public abstract class AccessibilityService extends android.app.Service {
|
||||||
ctor public AccessibilityService();
|
ctor public AccessibilityService();
|
||||||
|
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
|
||||||
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
|
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
|
||||||
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
|
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
|
||||||
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
|
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
|
||||||
@@ -4755,6 +4756,7 @@ package android.app {
|
|||||||
method public void clearWindowAnimationFrameStats();
|
method public void clearWindowAnimationFrameStats();
|
||||||
method public boolean clearWindowContentFrameStats(int);
|
method public boolean clearWindowContentFrameStats(int);
|
||||||
method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
|
method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
|
||||||
|
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
|
||||||
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
|
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
|
||||||
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
|
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
|
||||||
method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
|
method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
|
||||||
|
|||||||
@@ -525,6 +525,31 @@ public abstract class AccessibilityService extends Service {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the view that has the specified focus type. The search is performed
|
||||||
|
* across all windows.
|
||||||
|
* <p>
|
||||||
|
* <strong>Note:</strong> In order to access the windows your service has
|
||||||
|
* to declare the capability to retrieve window content by setting the
|
||||||
|
* {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
|
||||||
|
* property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
|
||||||
|
* Also the service has to opt-in to retrieve the interactive windows by
|
||||||
|
* setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
|
||||||
|
* flag.Otherwise, the search will be performed only in the active window.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
|
||||||
|
* {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
|
||||||
|
* @return The node info of the focused view or null.
|
||||||
|
*
|
||||||
|
* @see AccessibilityNodeInfo#FOCUS_INPUT
|
||||||
|
* @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
|
||||||
|
*/
|
||||||
|
public AccessibilityNodeInfo findFocus(int focus) {
|
||||||
|
return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
|
||||||
|
AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the an {@link AccessibilityServiceInfo} describing this
|
* Gets the an {@link AccessibilityServiceInfo} describing this
|
||||||
* {@link AccessibilityService}. This method is useful if one wants
|
* {@link AccessibilityService}. This method is useful if one wants
|
||||||
|
|||||||
@@ -296,6 +296,28 @@ public final class UiAutomation {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the view that has the specified focus type. The search is performed
|
||||||
|
* across all windows.
|
||||||
|
* <p>
|
||||||
|
* <strong>Note:</strong> In order to access the windows you have to opt-in
|
||||||
|
* to retrieve the interactive windows by setting the
|
||||||
|
* {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
|
||||||
|
* Otherwise, the search will be performed only in the active window.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
|
||||||
|
* {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
|
||||||
|
* @return The node info of the focused view or null.
|
||||||
|
*
|
||||||
|
* @see AccessibilityNodeInfo#FOCUS_INPUT
|
||||||
|
* @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
|
||||||
|
*/
|
||||||
|
public AccessibilityNodeInfo findFocus(int focus) {
|
||||||
|
return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
|
||||||
|
AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the an {@link AccessibilityServiceInfo} describing this UiAutomation.
|
* Gets the an {@link AccessibilityServiceInfo} describing this UiAutomation.
|
||||||
* This method is useful if one wants to change some of the dynamically
|
* This method is useful if one wants to change some of the dynamically
|
||||||
|
|||||||
@@ -8790,11 +8790,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
&& !pointInView(event.getX(), event.getY()))) {
|
&& !pointInView(event.getX(), event.getY()))) {
|
||||||
mSendingHoverAccessibilityEvents = false;
|
mSendingHoverAccessibilityEvents = false;
|
||||||
sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
|
sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
|
||||||
// If the window does not have input focus we take away accessibility
|
|
||||||
// focus as soon as the user stop hovering over the view.
|
|
||||||
if (mAttachInfo != null && !mAttachInfo.mHasWindowFocus) {
|
|
||||||
getViewRootImpl().setAccessibilityFocus(null, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,19 @@ public final class AccessibilityInteractionClient
|
|||||||
false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
|
false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the root {@link AccessibilityNodeInfo} in a given window.
|
||||||
|
*
|
||||||
|
* @param connectionId The id of a connection for interacting with the system.
|
||||||
|
* @param windowId The window id.
|
||||||
|
* @return The root {@link AccessibilityNodeInfo} if found, null otherwise.
|
||||||
|
*/
|
||||||
|
public AccessibilityNodeInfo getRootInWindow(int connectionId, int windowId) {
|
||||||
|
return findAccessibilityNodeInfoByAccessibilityId(connectionId, windowId,
|
||||||
|
AccessibilityNodeInfo.ROOT_NODE_ID, false,
|
||||||
|
AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the info for a window.
|
* Gets the info for a window.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ public class AccessibilityNodeInfo implements Parcelable {
|
|||||||
/** @hide */
|
/** @hide */
|
||||||
public static final int ACTIVE_WINDOW_ID = UNDEFINED_ITEM_ID;
|
public static final int ACTIVE_WINDOW_ID = UNDEFINED_ITEM_ID;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static final int ANY_WINDOW_ID = -2;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
|
public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
|
||||||
|
|
||||||
|
|||||||
@@ -166,8 +166,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
|
|
||||||
private final Point mTempPoint = new Point();
|
private final Point mTempPoint = new Point();
|
||||||
|
|
||||||
private final Display mDefaultDisplay;
|
|
||||||
|
|
||||||
private final PackageManager mPackageManager;
|
private final PackageManager mPackageManager;
|
||||||
|
|
||||||
private final WindowManagerInternal mWindowManagerService;
|
private final WindowManagerInternal mWindowManagerService;
|
||||||
@@ -176,7 +174,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
|
|
||||||
private final MainHandler mMainHandler;
|
private final MainHandler mMainHandler;
|
||||||
|
|
||||||
private Service mQueryBridge;
|
private InteractionBridge mInteractionBridge;
|
||||||
|
|
||||||
private AlertDialog mEnableTouchExplorationDialog;
|
private AlertDialog mEnableTouchExplorationDialog;
|
||||||
|
|
||||||
@@ -232,10 +230,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
|
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
|
||||||
mSecurityPolicy = new SecurityPolicy();
|
mSecurityPolicy = new SecurityPolicy();
|
||||||
mMainHandler = new MainHandler(mContext.getMainLooper());
|
mMainHandler = new MainHandler(mContext.getMainLooper());
|
||||||
//TODO: (multi-display) We need to support multiple displays.
|
|
||||||
DisplayManager displayManager = (DisplayManager)
|
|
||||||
mContext.getSystemService(Context.DISPLAY_SERVICE);
|
|
||||||
mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
|
|
||||||
registerBroadcastReceivers();
|
registerBroadcastReceivers();
|
||||||
new AccessibilityContentObserver(mMainHandler).register(
|
new AccessibilityContentObserver(mMainHandler).register(
|
||||||
context.getContentResolver());
|
context.getContentResolver());
|
||||||
@@ -401,7 +395,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
return true; // yes, recycle the event
|
return true; // yes, recycle the event
|
||||||
}
|
}
|
||||||
if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
|
if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
|
||||||
mSecurityPolicy.updateActiveWindowLocked(event.getWindowId(), event.getEventType());
|
mSecurityPolicy.updateActiveAndAccessibilityFocusedWindowLocked(event.getWindowId(),
|
||||||
|
event.getSourceNodeId(), event.getEventType());
|
||||||
mSecurityPolicy.updateEventSourceLocked(event);
|
mSecurityPolicy.updateEventSourceLocked(event);
|
||||||
notifyAccessibilityServicesDelayedLocked(event, false);
|
notifyAccessibilityServicesDelayedLocked(event, false);
|
||||||
notifyAccessibilityServicesDelayedLocked(event, true);
|
notifyAccessibilityServicesDelayedLocked(event, true);
|
||||||
@@ -725,46 +720,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
* @return Whether accessibility focus was found and the bounds are populated.
|
* @return Whether accessibility focus was found and the bounds are populated.
|
||||||
*/
|
*/
|
||||||
// TODO: (multi-display) Make sure this works for multiple displays.
|
// TODO: (multi-display) Make sure this works for multiple displays.
|
||||||
boolean getAccessibilityFocusBoundsInActiveWindow(Rect outBounds) {
|
boolean getAccessibilityFocusBounds(Rect outBounds) {
|
||||||
// Instead of keeping track of accessibility focus events per
|
return getInteractionBridgeLocked().getAccessibilityFocusBoundsNotLocked(outBounds);
|
||||||
// window to be able to find the focus in the active window,
|
|
||||||
// we take a stateless approach and look it up. This is fine
|
|
||||||
// since we do this only when the user clicks/long presses.
|
|
||||||
Service service = getQueryBridge();
|
|
||||||
final int connectionId = service.mId;
|
|
||||||
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
|
|
||||||
client.addConnection(connectionId, service);
|
|
||||||
try {
|
|
||||||
AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
|
|
||||||
.getRootInActiveWindow(connectionId);
|
|
||||||
if (root == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
AccessibilityNodeInfo focus = root.findFocus(
|
|
||||||
AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
|
|
||||||
if (focus == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
focus.getBoundsInScreen(outBounds);
|
|
||||||
|
|
||||||
MagnificationSpec spec = service.getCompatibleMagnificationSpec(focus.getWindowId());
|
|
||||||
if (spec != null && !spec.isNop()) {
|
|
||||||
outBounds.offset((int) -spec.offsetX, (int) -spec.offsetY);
|
|
||||||
outBounds.scale(1 / spec.scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clip to the window rectangle.
|
|
||||||
Rect windowBounds = mTempRect;
|
|
||||||
getActiveWindowBounds(windowBounds);
|
|
||||||
outBounds.intersect(windowBounds);
|
|
||||||
// Clip to the screen rectangle.
|
|
||||||
mDefaultDisplay.getRealSize(mTempPoint);
|
|
||||||
outBounds.intersect(0, 0, mTempPoint.x, mTempPoint.y);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} finally {
|
|
||||||
client.removeConnection(connectionId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -855,14 +812,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Service getQueryBridge() {
|
private InteractionBridge getInteractionBridgeLocked() {
|
||||||
if (mQueryBridge == null) {
|
if (mInteractionBridge == null) {
|
||||||
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
|
mInteractionBridge = new InteractionBridge();
|
||||||
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
|
|
||||||
mQueryBridge = new Service(UserHandle.USER_NULL,
|
|
||||||
sFakeAccessibilityServiceComponentName, info);
|
|
||||||
}
|
}
|
||||||
return mQueryBridge;
|
return mInteractionBridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
|
private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
|
||||||
@@ -1333,9 +1287,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
private void onUserStateChangedLocked(UserState userState) {
|
private void onUserStateChangedLocked(UserState userState) {
|
||||||
// TODO: Remove this hack
|
// TODO: Remove this hack
|
||||||
mInitialized = true;
|
mInitialized = true;
|
||||||
updateLegacyCapabilities(userState);
|
updateLegacyCapabilitiesLocked(userState);
|
||||||
updateServicesLocked(userState);
|
updateServicesLocked(userState);
|
||||||
updateWindowsForAccessibilityCallback(userState);
|
updateWindowsForAccessibilityCallbackLocked(userState);
|
||||||
|
updateAccessibilityFocusBehaviorLocked(userState);
|
||||||
updateFilterKeyEventsLocked(userState);
|
updateFilterKeyEventsLocked(userState);
|
||||||
updateTouchExplorationLocked(userState);
|
updateTouchExplorationLocked(userState);
|
||||||
updateEnhancedWebAccessibilityLocked(userState);
|
updateEnhancedWebAccessibilityLocked(userState);
|
||||||
@@ -1344,7 +1299,29 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
scheduleUpdateClientsIfNeededLocked(userState);
|
scheduleUpdateClientsIfNeededLocked(userState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateWindowsForAccessibilityCallback(UserState userState) {
|
private void updateAccessibilityFocusBehaviorLocked(UserState userState) {
|
||||||
|
// If there is no service that can operate with interactive windows
|
||||||
|
// then we keep the old behavior where a window loses accessibility
|
||||||
|
// focus if it is no longer active. This still changes the behavior
|
||||||
|
// for services that do not operate with interactive windows and run
|
||||||
|
// at the same time as the one(s) which does. In practice however,
|
||||||
|
// there is only one service that uses accessibility focus and it
|
||||||
|
// is typically the one that operates with interactive windows, So,
|
||||||
|
// this is fine. Note that to allow a service to work across windows
|
||||||
|
// we have to allow accessibility focus stay in any of them. Sigh...
|
||||||
|
List<Service> boundServices = userState.mBoundServices;
|
||||||
|
final int boundServiceCount = boundServices.size();
|
||||||
|
for (int i = 0; i < boundServiceCount; i++) {
|
||||||
|
Service boundService = boundServices.get(i);
|
||||||
|
if (boundService.canRetrieveInteractiveWindowsLocked()) {
|
||||||
|
userState.mAccessibilityFocusOnlyInActiveWindow = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userState.mAccessibilityFocusOnlyInActiveWindow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateWindowsForAccessibilityCallbackLocked(UserState userState) {
|
||||||
if (userState.mIsAccessibilityEnabled) {
|
if (userState.mIsAccessibilityEnabled) {
|
||||||
// We observe windows for accessibility only if there is at least
|
// We observe windows for accessibility only if there is at least
|
||||||
// one bound service that can retrieve window content that specified
|
// one bound service that can retrieve window content that specified
|
||||||
@@ -1357,8 +1334,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
final int boundServiceCount = boundServices.size();
|
final int boundServiceCount = boundServices.size();
|
||||||
for (int i = 0; i < boundServiceCount; i++) {
|
for (int i = 0; i < boundServiceCount; i++) {
|
||||||
Service boundService = boundServices.get(i);
|
Service boundService = boundServices.get(i);
|
||||||
if (mSecurityPolicy.canRetrieveWindowContentLocked(boundService)
|
if (boundService.canRetrieveInteractiveWindowsLocked()) {
|
||||||
&& boundService.mRetrieveInteractiveWindows) {
|
|
||||||
boundServiceCanRetrieveInteractiveWindows = true;
|
boundServiceCanRetrieveInteractiveWindows = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1381,7 +1357,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLegacyCapabilities(UserState userState) {
|
private void updateLegacyCapabilitiesLocked(UserState userState) {
|
||||||
// Up to JB-MR1 we had a white list with services that can enable touch
|
// Up to JB-MR1 we had a white list with services that can enable touch
|
||||||
// exploration. When a service is first started we show a dialog to the
|
// exploration. When a service is first started we show a dialog to the
|
||||||
// use to get a permission to white list the service.
|
// use to get a permission to white list the service.
|
||||||
@@ -1582,6 +1558,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
DisplayAdjustmentUtils.applyAdjustments(mContext, userState.mUserId);
|
DisplayAdjustmentUtils.applyAdjustments(mContext, userState.mUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
|
||||||
|
IBinder windowToken = mGlobalWindowTokens.get(windowId);
|
||||||
|
if (windowToken == null) {
|
||||||
|
windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
|
||||||
|
}
|
||||||
|
if (windowToken != null) {
|
||||||
|
return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
|
||||||
|
windowToken);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
|
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
|
||||||
mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
|
mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
|
||||||
@@ -1658,6 +1646,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
public static final int MSG_UPDATE_INPUT_FILTER = 6;
|
public static final int MSG_UPDATE_INPUT_FILTER = 6;
|
||||||
public static final int MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG = 7;
|
public static final int MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG = 7;
|
||||||
public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8;
|
public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8;
|
||||||
|
public static final int MSG_CLEAR_ACCESSIBILITY_FOCUS = 9;
|
||||||
|
|
||||||
public MainHandler(Looper looper) {
|
public MainHandler(Looper looper) {
|
||||||
super(looper);
|
super(looper);
|
||||||
@@ -1713,6 +1702,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
Service service = (Service) msg.obj;
|
Service service = (Service) msg.obj;
|
||||||
showEnableTouchExplorationDialog(service);
|
showEnableTouchExplorationDialog(service);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case MSG_CLEAR_ACCESSIBILITY_FOCUS: {
|
||||||
|
final int windowId = msg.arg1;
|
||||||
|
InteractionBridge bridge;
|
||||||
|
synchronized (mLock) {
|
||||||
|
bridge = getInteractionBridgeLocked();
|
||||||
|
}
|
||||||
|
bridge.clearAccessibilityFocusNotLocked(windowId);
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1981,6 +1979,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canRetrieveInteractiveWindowsLocked() {
|
||||||
|
return mSecurityPolicy.canRetrieveWindowContentLocked(this)
|
||||||
|
&& mRetrieveInteractiveWindows;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setServiceInfo(AccessibilityServiceInfo info) {
|
public void setServiceInfo(AccessibilityServiceInfo info) {
|
||||||
final long identity = Binder.clearCallingIdentity();
|
final long identity = Binder.clearCallingIdentity();
|
||||||
@@ -2109,7 +2112,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
final int interrogatingPid = Binder.getCallingPid();
|
final int interrogatingPid = Binder.getCallingPid();
|
||||||
final long identityToken = Binder.clearCallingIdentity();
|
final long identityToken = Binder.clearCallingIdentity();
|
||||||
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId);
|
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
|
||||||
try {
|
try {
|
||||||
connection.findAccessibilityNodeInfosByViewId(accessibilityNodeId,
|
connection.findAccessibilityNodeInfosByViewId(accessibilityNodeId,
|
||||||
viewIdResName, interactionId, callback, mFetchFlags, interrogatingPid,
|
viewIdResName, interactionId, callback, mFetchFlags, interrogatingPid,
|
||||||
@@ -2153,7 +2156,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
final int interrogatingPid = Binder.getCallingPid();
|
final int interrogatingPid = Binder.getCallingPid();
|
||||||
final long identityToken = Binder.clearCallingIdentity();
|
final long identityToken = Binder.clearCallingIdentity();
|
||||||
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId);
|
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
|
||||||
try {
|
try {
|
||||||
connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
|
connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
|
||||||
interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
|
interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
|
||||||
@@ -2197,7 +2200,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
final int interrogatingPid = Binder.getCallingPid();
|
final int interrogatingPid = Binder.getCallingPid();
|
||||||
final long identityToken = Binder.clearCallingIdentity();
|
final long identityToken = Binder.clearCallingIdentity();
|
||||||
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId);
|
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
|
||||||
try {
|
try {
|
||||||
connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
|
connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
|
||||||
interactionId, callback, mFetchFlags | flags, interrogatingPid,
|
interactionId, callback, mFetchFlags | flags, interrogatingPid,
|
||||||
@@ -2227,7 +2230,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
if (resolvedUserId != mCurrentUserId) {
|
if (resolvedUserId != mCurrentUserId) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
|
resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked(
|
||||||
|
accessibilityWindowId, focusType);
|
||||||
final boolean permissionGranted =
|
final boolean permissionGranted =
|
||||||
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
|
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
|
||||||
if (!permissionGranted) {
|
if (!permissionGranted) {
|
||||||
@@ -2241,14 +2245,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
final int interrogatingPid = Binder.getCallingPid();
|
final int interrogatingPid = Binder.getCallingPid();
|
||||||
final long identityToken = Binder.clearCallingIdentity();
|
final long identityToken = Binder.clearCallingIdentity();
|
||||||
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId);
|
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
|
||||||
try {
|
try {
|
||||||
connection.findFocus(accessibilityNodeId, focusType, interactionId, callback,
|
connection.findFocus(accessibilityNodeId, focusType, interactionId, callback,
|
||||||
mFetchFlags, interrogatingPid, interrogatingTid, spec);
|
mFetchFlags, interrogatingPid, interrogatingTid, spec);
|
||||||
return true;
|
return true;
|
||||||
} catch (RemoteException re) {
|
} catch (RemoteException re) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.e(LOG_TAG, "Error calling findAccessibilityFocus()");
|
Slog.e(LOG_TAG, "Error calling findFocus()");
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(identityToken);
|
Binder.restoreCallingIdentity(identityToken);
|
||||||
@@ -2284,7 +2288,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
final int interrogatingPid = Binder.getCallingPid();
|
final int interrogatingPid = Binder.getCallingPid();
|
||||||
final long identityToken = Binder.clearCallingIdentity();
|
final long identityToken = Binder.clearCallingIdentity();
|
||||||
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId);
|
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
|
||||||
try {
|
try {
|
||||||
connection.focusSearch(accessibilityNodeId, direction, interactionId, callback,
|
connection.focusSearch(accessibilityNodeId, direction, interactionId, callback,
|
||||||
mFetchFlags, interrogatingPid, interrogatingTid, spec);
|
mFetchFlags, interrogatingPid, interrogatingTid, spec);
|
||||||
@@ -2729,16 +2733,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
return accessibilityWindowId;
|
return accessibilityWindowId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MagnificationSpec getCompatibleMagnificationSpec(int windowId) {
|
private int resolveAccessibilityWindowIdForFindFocusLocked(int windowId, int focusType) {
|
||||||
IBinder windowToken = mGlobalWindowTokens.get(windowId);
|
if (windowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) {
|
||||||
if (windowToken == null) {
|
return mSecurityPolicy.mActiveWindowId;
|
||||||
windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
|
|
||||||
}
|
}
|
||||||
if (windowToken != null) {
|
if (windowId == AccessibilityNodeInfo.ANY_WINDOW_ID) {
|
||||||
return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
|
if (focusType == AccessibilityNodeInfo.FOCUS_INPUT) {
|
||||||
windowToken);
|
return mSecurityPolicy.mFocusedWindowId;
|
||||||
|
} else if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) {
|
||||||
|
return mSecurityPolicy.mAccessibilityFocusedWindowId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return windowId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class InvocationHandler extends Handler {
|
private final class InvocationHandler extends Handler {
|
||||||
@@ -3031,7 +3037,86 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class InteractionBridge {
|
||||||
|
private final Display mDefaultDisplay;
|
||||||
|
private final int mConnectionId;
|
||||||
|
private final AccessibilityInteractionClient mClient;
|
||||||
|
|
||||||
|
public InteractionBridge() {
|
||||||
|
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
|
||||||
|
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
|
||||||
|
info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
|
||||||
|
info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
|
||||||
|
Service service = new Service(UserHandle.USER_NULL,
|
||||||
|
sFakeAccessibilityServiceComponentName, info);
|
||||||
|
|
||||||
|
mConnectionId = service.mId;
|
||||||
|
|
||||||
|
mClient = AccessibilityInteractionClient.getInstance();
|
||||||
|
mClient.addConnection(mConnectionId, service);
|
||||||
|
|
||||||
|
//TODO: (multi-display) We need to support multiple displays.
|
||||||
|
DisplayManager displayManager = (DisplayManager)
|
||||||
|
mContext.getSystemService(Context.DISPLAY_SERVICE);
|
||||||
|
mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearAccessibilityFocusNotLocked(int windowId) {
|
||||||
|
AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked(windowId);
|
||||||
|
if (focus != null) {
|
||||||
|
focus.performAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getAccessibilityFocusBoundsNotLocked(Rect outBounds) {
|
||||||
|
AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked();
|
||||||
|
if (focus == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (mLock) {
|
||||||
|
focus.getBoundsInScreen(outBounds);
|
||||||
|
|
||||||
|
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId());
|
||||||
|
if (spec != null && !spec.isNop()) {
|
||||||
|
outBounds.offset((int) -spec.offsetX, (int) -spec.offsetY);
|
||||||
|
outBounds.scale(1 / spec.scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clip to the window rectangle.
|
||||||
|
Rect windowBounds = mTempRect;
|
||||||
|
getActiveWindowBounds(windowBounds);
|
||||||
|
outBounds.intersect(windowBounds);
|
||||||
|
|
||||||
|
// Clip to the screen rectangle.
|
||||||
|
mDefaultDisplay.getRealSize(mTempPoint);
|
||||||
|
outBounds.intersect(0, 0, mTempPoint.x, mTempPoint.y);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccessibilityNodeInfo getAccessibilityFocusNotLocked() {
|
||||||
|
final int focusedWindowId;
|
||||||
|
synchronized (mLock) {
|
||||||
|
focusedWindowId = mSecurityPolicy.mAccessibilityFocusedWindowId;
|
||||||
|
if (focusedWindowId == SecurityPolicy.INVALID_WINDOW_ID) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getAccessibilityFocusNotLocked(focusedWindowId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccessibilityNodeInfo getAccessibilityFocusNotLocked(int windowId) {
|
||||||
|
return mClient.findFocus(mConnectionId,
|
||||||
|
windowId, AccessibilityNodeInfo.ROOT_NODE_ID,
|
||||||
|
AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class SecurityPolicy {
|
final class SecurityPolicy {
|
||||||
|
public static final int INVALID_WINDOW_ID = -1;
|
||||||
|
|
||||||
private static final int VALID_ACTIONS =
|
private static final int VALID_ACTIONS =
|
||||||
AccessibilityNodeInfo.ACTION_CLICK
|
AccessibilityNodeInfo.ACTION_CLICK
|
||||||
| AccessibilityNodeInfo.ACTION_LONG_CLICK
|
| AccessibilityNodeInfo.ACTION_LONG_CLICK
|
||||||
@@ -3075,8 +3160,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
public final List<AccessibilityWindowInfo> mWindows =
|
public final List<AccessibilityWindowInfo> mWindows =
|
||||||
new ArrayList<AccessibilityWindowInfo>();
|
new ArrayList<AccessibilityWindowInfo>();
|
||||||
|
|
||||||
public int mActiveWindowId;
|
public int mActiveWindowId = INVALID_WINDOW_ID;
|
||||||
public int mFocusedWindowId;
|
public int mFocusedWindowId = INVALID_WINDOW_ID;
|
||||||
|
public int mAccessibilityFocusedWindowId = INVALID_WINDOW_ID;
|
||||||
|
public long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
|
||||||
|
|
||||||
public AccessibilityEvent mShowingFocusedWindowEvent;
|
public AccessibilityEvent mShowingFocusedWindowEvent;
|
||||||
|
|
||||||
private boolean mTouchInteractionInProgress;
|
private boolean mTouchInteractionInProgress;
|
||||||
@@ -3129,9 +3217,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
mWindows.remove(i).recycle();
|
mWindows.remove(i).recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
mFocusedWindowId = -1;
|
mFocusedWindowId = INVALID_WINDOW_ID;
|
||||||
if (!mTouchInteractionInProgress) {
|
if (!mTouchInteractionInProgress) {
|
||||||
mActiveWindowId = -1;
|
mActiveWindowId = INVALID_WINDOW_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the active window goes away while the user is touch exploring we
|
// If the active window goes away while the user is touch exploring we
|
||||||
@@ -3159,7 +3247,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mTouchInteractionInProgress && activeWindowGone) {
|
if (mTouchInteractionInProgress && activeWindowGone) {
|
||||||
mActiveWindowId = -1;
|
mActiveWindowId = INVALID_WINDOW_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Focused window may change the active one, so set the
|
// Focused window may change the active one, so set the
|
||||||
@@ -3193,7 +3281,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateActiveWindowLocked(int windowId, int eventType) {
|
public void updateActiveAndAccessibilityFocusedWindowLocked(int windowId, long nodeId,
|
||||||
|
int eventType) {
|
||||||
// The active window is either the window that has input focus or
|
// The active window is either the window that has input focus or
|
||||||
// the window that the user is currently touching. If the user is
|
// the window that the user is currently touching. If the user is
|
||||||
// touching a window that does not have input focus as soon as the
|
// touching a window that does not have input focus as soon as the
|
||||||
@@ -3228,6 +3317,29 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mAccessibilityFocusedWindowId != windowId) {
|
||||||
|
mMainHandler.obtainMessage(MainHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS,
|
||||||
|
mAccessibilityFocusedWindowId, 0).sendToTarget();
|
||||||
|
mAccessibilityFocusedWindowId = windowId;
|
||||||
|
mAccessibilityFocusNodeId = nodeId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mAccessibilityFocusNodeId == nodeId) {
|
||||||
|
mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
|
||||||
|
}
|
||||||
|
if (mAccessibilityFocusNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID
|
||||||
|
&& mAccessibilityFocusedWindowId == windowId) {
|
||||||
|
mAccessibilityFocusedWindowId = INVALID_WINDOW_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3248,7 +3360,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
// (they are a result of user touching the screen) so change of
|
// (they are a result of user touching the screen) so change of
|
||||||
// the active window before all hover accessibility events from
|
// the active window before all hover accessibility events from
|
||||||
// the touched window are delivered is fine.
|
// the touched window are delivered is fine.
|
||||||
|
final int oldActiveWindow = mSecurityPolicy.mActiveWindowId;
|
||||||
setActiveWindowLocked(mFocusedWindowId);
|
setActiveWindowLocked(mFocusedWindowId);
|
||||||
|
|
||||||
|
// If there is no service that can operate with active windows
|
||||||
|
// we keep accessibility focus behavior to constrain it only in
|
||||||
|
// the active window. Look at updateAccessibilityFocusBehaviorLocked
|
||||||
|
// for details.
|
||||||
|
if (oldActiveWindow != mSecurityPolicy.mActiveWindowId
|
||||||
|
&& mAccessibilityFocusedWindowId == oldActiveWindow
|
||||||
|
&& getCurrentUserStateLocked().mAccessibilityFocusOnlyInActiveWindow) {
|
||||||
|
mMainHandler.obtainMessage(MainHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS,
|
||||||
|
oldActiveWindow, 0).sendToTarget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3403,6 +3527,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
|||||||
public boolean mIsDisplayMagnificationEnabled;
|
public boolean mIsDisplayMagnificationEnabled;
|
||||||
public boolean mIsFilterKeyEventsEnabled;
|
public boolean mIsFilterKeyEventsEnabled;
|
||||||
public boolean mHasDisplayColorAdjustment;
|
public boolean mHasDisplayColorAdjustment;
|
||||||
|
public boolean mAccessibilityFocusOnlyInActiveWindow;
|
||||||
|
|
||||||
private Service mUiAutomationService;
|
private Service mUiAutomationService;
|
||||||
private IAccessibilityServiceClient mUiAutomationServiceClient;
|
private IAccessibilityServiceClient mUiAutomationServiceClient;
|
||||||
|
|||||||
@@ -1159,7 +1159,7 @@ class TouchExplorer implements EventStreamTransformation {
|
|||||||
// No last touch explored event but there is accessibility focus in
|
// No last touch explored event but there is accessibility focus in
|
||||||
// the active window. We click in the middle of the focus bounds.
|
// the active window. We click in the middle of the focus bounds.
|
||||||
Rect focusBounds = mTempRect;
|
Rect focusBounds = mTempRect;
|
||||||
if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) {
|
if (mAms.getAccessibilityFocusBounds(focusBounds)) {
|
||||||
clickLocationX = focusBounds.centerX();
|
clickLocationX = focusBounds.centerX();
|
||||||
clickLocationY = focusBounds.centerY();
|
clickLocationY = focusBounds.centerY();
|
||||||
} else {
|
} else {
|
||||||
@@ -1177,7 +1177,7 @@ class TouchExplorer implements EventStreamTransformation {
|
|||||||
mAms.getActiveWindowBounds(activeWindowBounds);
|
mAms.getActiveWindowBounds(activeWindowBounds);
|
||||||
if (activeWindowBounds.contains(clickLocationX, clickLocationY)) {
|
if (activeWindowBounds.contains(clickLocationX, clickLocationY)) {
|
||||||
Rect focusBounds = mTempRect;
|
Rect focusBounds = mTempRect;
|
||||||
if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) {
|
if (mAms.getAccessibilityFocusBounds(focusBounds)) {
|
||||||
if (!focusBounds.contains(clickLocationX, clickLocationY)) {
|
if (!focusBounds.contains(clickLocationX, clickLocationY)) {
|
||||||
clickLocationX = focusBounds.centerX();
|
clickLocationX = focusBounds.centerX();
|
||||||
clickLocationY = focusBounds.centerY();
|
clickLocationY = focusBounds.centerY();
|
||||||
@@ -1332,7 +1332,7 @@ class TouchExplorer implements EventStreamTransformation {
|
|||||||
// No last touch explored event but there is accessibility focus in
|
// No last touch explored event but there is accessibility focus in
|
||||||
// the active window. We click in the middle of the focus bounds.
|
// the active window. We click in the middle of the focus bounds.
|
||||||
Rect focusBounds = mTempRect;
|
Rect focusBounds = mTempRect;
|
||||||
if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) {
|
if (mAms.getAccessibilityFocusBounds(focusBounds)) {
|
||||||
clickLocationX = focusBounds.centerX();
|
clickLocationX = focusBounds.centerX();
|
||||||
clickLocationY = focusBounds.centerY();
|
clickLocationY = focusBounds.centerY();
|
||||||
} else {
|
} else {
|
||||||
@@ -1350,7 +1350,7 @@ class TouchExplorer implements EventStreamTransformation {
|
|||||||
mAms.getActiveWindowBounds(activeWindowBounds);
|
mAms.getActiveWindowBounds(activeWindowBounds);
|
||||||
if (activeWindowBounds.contains(clickLocationX, clickLocationY)) {
|
if (activeWindowBounds.contains(clickLocationX, clickLocationY)) {
|
||||||
Rect focusBounds = mTempRect;
|
Rect focusBounds = mTempRect;
|
||||||
if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) {
|
if (mAms.getAccessibilityFocusBounds(focusBounds)) {
|
||||||
if (!focusBounds.contains(clickLocationX, clickLocationY)) {
|
if (!focusBounds.contains(clickLocationX, clickLocationY)) {
|
||||||
clickLocationX = focusBounds.centerX();
|
clickLocationX = focusBounds.centerX();
|
||||||
clickLocationY = focusBounds.centerY();
|
clickLocationY = focusBounds.centerY();
|
||||||
|
|||||||
Reference in New Issue
Block a user