Loading core/java/android/view/MotionEvent.java +29 −0 Original line number Diff line number Diff line Loading @@ -486,6 +486,21 @@ public final class MotionEvent extends InputEvent implements Parcelable { */ public static final int FLAG_TAINTED = 0x80000000; /** * Private flag indicating that this event was synthesized by the system and should be delivered * to the accessibility focused view first. When being dispatched such an event is not handled * by predecessors of the accessibility focused view and after the event reaches that view the * flag is cleared and normal event dispatch is performed. This ensures that the platform can * click on any view that has accessibility focus which is semantically equivalent to asking the * view to perform a click accessibility action but more generic as views not implementing click * action correctly can still be activated. * * @hide * @see #isTargetAccessibilityFocus() * @see #setTargetAccessibilityFocus(boolean) */ public static final int FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000; /** * Flag indicating the motion event intersected the top edge of the screen. */ Loading Loading @@ -2139,6 +2154,20 @@ public final class MotionEvent extends InputEvent implements Parcelable { nativeSetFlags(mNativePtr, tainted ? flags | FLAG_TAINTED : flags & ~FLAG_TAINTED); } /** @hide */ public boolean isTargetAccessibilityFocus() { final int flags = getFlags(); return (flags & FLAG_TARGET_ACCESSIBILITY_FOCUS) != 0; } /** @hide */ public void setTargetAccessibilityFocus(boolean targetsFocus) { final int flags = getFlags(); nativeSetFlags(mNativePtr, targetsFocus ? flags | FLAG_TARGET_ACCESSIBILITY_FOCUS : flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS); } /** @hide */ public final boolean isHoverExitPending() { final int flags = getFlags(); Loading core/java/android/view/View.java +8 −0 Original line number Diff line number Diff line Loading @@ -14391,6 +14391,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public boolean dispatchTouchEvent(MotionEvent event) { // If the event should be handled by accessibility focus first. if (event.isTargetAccessibilityFocus()) { // We don't have focus or no virtual descendant has it, do not handle the event. if (!isAccessibilityFocusedViewOrHost()) { return false; } // We have focus and got the event, then use normal event dispatch. event.setTargetAccessibilityFocus(false); } boolean result = false; if (mInputEventConsistencyVerifier != null) { core/java/android/view/ViewGroup.java +75 −3 Original line number Diff line number Diff line Loading @@ -2048,8 +2048,26 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); View childWithAccessibilityFocus = event.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null; if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { // If there is a view that has accessibility focus we want it // to get the event first and if not handled we will perform a // normal dispatch. We may do a double iteration but this is // safer given the timeframe. if (childWithAccessibilityFocus != null) { if (childWithAccessibilityFocus != child) { continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } event.setTargetAccessibilityFocus(false); continue; } final PointerIcon pointerIcon = Loading Loading @@ -2617,6 +2635,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } // If the event targets the accessibility focused view and this is it, start // normal event dispatch. Maybe a descendant is what will handle the click. if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) { ev.setTargetAccessibilityFocus(false); } boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); Loading Loading @@ -2647,6 +2671,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // so this view group continues to intercept touches. intercepted = true; } // If intercepted, start normal event dispatch. Also if there is already // a view that is handling the gesture, do normal event dispatch. if (intercepted || mFirstTouchTarget != null) { ev.setTargetAccessibilityFocus(false); } // Check for cancelation. final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; Loading @@ -2658,6 +2689,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; if (!canceled && !intercepted) { // If the event is targeting accessibility focus we give it to the // view that has accessibility focus and if it does not handle it // we clear the flag and dispatch the event to all children as usual. // We are looking up the accessibility focused host to avoid keeping // state since these events are very rare. View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null; if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { Loading Loading @@ -2720,6 +2759,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager alreadyDispatchedToNewTouchTarget = true; break; } // The accessibility focus didn't handle the event, so clear // the flag and do a normal dispatch to all children. ev.setTargetAccessibilityFocus(false); } if (preorderedList != null) preorderedList.clear(); } Loading Loading @@ -2803,6 +2846,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return buildOrderedChildList(); } /** * Finds the child which has accessibility focus. * * @return The child that has focus. */ private View findChildWithAccessibilityFocus() { ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot == null) { return null; } View current = viewRoot.getAccessibilityFocusedHost(); if (current == null) { return null; } ViewParent parent = current.getParent(); while (parent instanceof View) { if (parent == this) { return current; } current = (View) parent; parent = current.getParent(); } return null; } /** * Resets all touch state in preparation for a new cycle. */ Loading Loading @@ -4923,7 +4994,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (params == null) { params = generateDefaultLayoutParams(); if (params == null) { throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); throw new IllegalArgumentException( "generateDefaultLayoutParams() cannot return null "); } } addView(child, index, params); Loading diffdeleted 100644 → 0 +0 −25 Original line number Diff line number Diff line diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index fc43882..832dc91 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -67,6 +67,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.NoSuchElementException; /** * This class represents an accessibility client - either an AccessibilityService or a UiAutomation. @@ -978,7 +979,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ /* ignore */ } if (mService != null) { - mService.unlinkToDeath(this, 0); + try { + mService.unlinkToDeath(this, 0); + }catch(NoSuchElementException e) { + Slog.e(LOG_TAG, "Failed unregistering death link"); + } mService = null; } services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +96 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,8 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.database.ContentObserver; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.IFingerprintService; Loading Loading @@ -194,6 +196,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR); private final Rect mTempRect = new Rect(); private final Rect mTempRect1 = new Rect(); private final PackageManager mPackageManager; private final PowerManager mPowerManager; Loading Loading @@ -251,6 +256,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub //TODO: Remove this hack private boolean mInitialized; private Point mTempPoint = new Point(); private boolean mIsAccessibilityButtonShown; private AccessibilityUserState getCurrentUserStateLocked() { Loading Loading @@ -1085,6 +1091,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return motionEventInjector; } /** * Gets a point within the accessibility focused node where we can send down * and up events to perform a click. * * @param outPoint The click point to populate. * @return Whether accessibility a click point was found and set. */ // TODO: (multi-display) Make sure this works for multiple displays. public boolean getAccessibilityFocusClickPointInScreen(Point outPoint) { return getInteractionBridge().getAccessibilityFocusClickPointInScreenNotLocked(outPoint); } /** * Perform an accessibility action on the view that currently has accessibility focus. * Has no effect if no item has accessibility focus, if the item with accessibility Loading @@ -1099,6 +1117,32 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return getInteractionBridge().performActionOnAccessibilityFocusedItemNotLocked(action); } /** * Returns true if accessibility focus is confined to the active window. */ public boolean accessibilityFocusOnlyInActiveWindow() { synchronized (mLock) { return mA11yWindowManager.isTrackingWindowsLocked(); } } /** * Gets the bounds of a window. * * @param outBounds The output to which to write the bounds. */ boolean getWindowBounds(int windowId, Rect outBounds) { IBinder token; synchronized (mLock) { token = getWindowToken(windowId, mCurrentUserId); } mWindowManagerService.getWindowFrame(token, outBounds); if (!outBounds.isEmpty()) { return true; } return false; } public int getActiveWindowId() { return mA11yWindowManager.getActiveWindowId(mCurrentUserId); } Loading Loading @@ -1877,9 +1921,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub for (int i = 0; !observingWindows && (i < boundServiceCount); i++) { AccessibilityServiceConnection boundService = boundServices.get(i); if (boundService.canRetrieveInteractiveWindowsLocked()) { userState.setAccessibilityFocusOnlyInActiveWindow(false); observingWindows = true; } } userState.setAccessibilityFocusOnlyInActiveWindow(true); // Gets all valid displays and start tracking windows of each display if there is at least // one bound service that can retrieve window content. Loading Loading @@ -2996,6 +3042,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY); } /** * Gets a point within the accessibility focused node where we can send down and up events * to perform a click. * * @param outPoint The click point to populate. * @return Whether accessibility a click point was found and set. */ // TODO: (multi-display) Make sure this works for multiple displays. boolean getAccessibilityFocusClickPointInScreen(Point outPoint) { return getInteractionBridge() .getAccessibilityFocusClickPointInScreenNotLocked(outPoint); } /** * Perform an accessibility action on the view that currently has accessibility focus. * Has no effect if no item has accessibility focus, if the item with accessibility Loading @@ -3014,6 +3073,43 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return focus.performAction(action.getId()); } public boolean getAccessibilityFocusClickPointInScreenNotLocked(Point outPoint) { AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked(); if (focus == null) { return false; } synchronized (mLock) { Rect boundsInScreen = mTempRect; focus.getBoundsInScreen(boundsInScreen); // Apply magnification if needed. MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId()); if (spec != null && !spec.isNop()) { boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY); boundsInScreen.scale(1 / spec.scale); } // Clip to the window bounds. Rect windowBounds = mTempRect1; getWindowBounds(focus.getWindowId(), windowBounds); if (!boundsInScreen.intersect(windowBounds)) { return false; } // Clip to the screen bounds. Point screenSize = mTempPoint; mDefaultDisplay.getRealSize(screenSize); if (!boundsInScreen.intersect(0, 0, screenSize.x, screenSize.y)) { return false; } outPoint.set(boundsInScreen.centerX(), boundsInScreen.centerY()); } return true; } private AccessibilityNodeInfo getAccessibilityFocusNotLocked() { final int focusedWindowId; synchronized (mLock) { Loading Loading
core/java/android/view/MotionEvent.java +29 −0 Original line number Diff line number Diff line Loading @@ -486,6 +486,21 @@ public final class MotionEvent extends InputEvent implements Parcelable { */ public static final int FLAG_TAINTED = 0x80000000; /** * Private flag indicating that this event was synthesized by the system and should be delivered * to the accessibility focused view first. When being dispatched such an event is not handled * by predecessors of the accessibility focused view and after the event reaches that view the * flag is cleared and normal event dispatch is performed. This ensures that the platform can * click on any view that has accessibility focus which is semantically equivalent to asking the * view to perform a click accessibility action but more generic as views not implementing click * action correctly can still be activated. * * @hide * @see #isTargetAccessibilityFocus() * @see #setTargetAccessibilityFocus(boolean) */ public static final int FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000; /** * Flag indicating the motion event intersected the top edge of the screen. */ Loading Loading @@ -2139,6 +2154,20 @@ public final class MotionEvent extends InputEvent implements Parcelable { nativeSetFlags(mNativePtr, tainted ? flags | FLAG_TAINTED : flags & ~FLAG_TAINTED); } /** @hide */ public boolean isTargetAccessibilityFocus() { final int flags = getFlags(); return (flags & FLAG_TARGET_ACCESSIBILITY_FOCUS) != 0; } /** @hide */ public void setTargetAccessibilityFocus(boolean targetsFocus) { final int flags = getFlags(); nativeSetFlags(mNativePtr, targetsFocus ? flags | FLAG_TARGET_ACCESSIBILITY_FOCUS : flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS); } /** @hide */ public final boolean isHoverExitPending() { final int flags = getFlags(); Loading
core/java/android/view/View.java +8 −0 Original line number Diff line number Diff line Loading @@ -14391,6 +14391,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public boolean dispatchTouchEvent(MotionEvent event) { // If the event should be handled by accessibility focus first. if (event.isTargetAccessibilityFocus()) { // We don't have focus or no virtual descendant has it, do not handle the event. if (!isAccessibilityFocusedViewOrHost()) { return false; } // We have focus and got the event, then use normal event dispatch. event.setTargetAccessibilityFocus(false); } boolean result = false; if (mInputEventConsistencyVerifier != null) {
core/java/android/view/ViewGroup.java +75 −3 Original line number Diff line number Diff line Loading @@ -2048,8 +2048,26 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); View childWithAccessibilityFocus = event.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null; if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { // If there is a view that has accessibility focus we want it // to get the event first and if not handled we will perform a // normal dispatch. We may do a double iteration but this is // safer given the timeframe. if (childWithAccessibilityFocus != null) { if (childWithAccessibilityFocus != child) { continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } event.setTargetAccessibilityFocus(false); continue; } final PointerIcon pointerIcon = Loading Loading @@ -2617,6 +2635,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } // If the event targets the accessibility focused view and this is it, start // normal event dispatch. Maybe a descendant is what will handle the click. if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) { ev.setTargetAccessibilityFocus(false); } boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); Loading Loading @@ -2647,6 +2671,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // so this view group continues to intercept touches. intercepted = true; } // If intercepted, start normal event dispatch. Also if there is already // a view that is handling the gesture, do normal event dispatch. if (intercepted || mFirstTouchTarget != null) { ev.setTargetAccessibilityFocus(false); } // Check for cancelation. final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; Loading @@ -2658,6 +2689,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; if (!canceled && !intercepted) { // If the event is targeting accessibility focus we give it to the // view that has accessibility focus and if it does not handle it // we clear the flag and dispatch the event to all children as usual. // We are looking up the accessibility focused host to avoid keeping // state since these events are very rare. View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null; if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { Loading Loading @@ -2720,6 +2759,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager alreadyDispatchedToNewTouchTarget = true; break; } // The accessibility focus didn't handle the event, so clear // the flag and do a normal dispatch to all children. ev.setTargetAccessibilityFocus(false); } if (preorderedList != null) preorderedList.clear(); } Loading Loading @@ -2803,6 +2846,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return buildOrderedChildList(); } /** * Finds the child which has accessibility focus. * * @return The child that has focus. */ private View findChildWithAccessibilityFocus() { ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot == null) { return null; } View current = viewRoot.getAccessibilityFocusedHost(); if (current == null) { return null; } ViewParent parent = current.getParent(); while (parent instanceof View) { if (parent == this) { return current; } current = (View) parent; parent = current.getParent(); } return null; } /** * Resets all touch state in preparation for a new cycle. */ Loading Loading @@ -4923,7 +4994,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (params == null) { params = generateDefaultLayoutParams(); if (params == null) { throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); throw new IllegalArgumentException( "generateDefaultLayoutParams() cannot return null "); } } addView(child, index, params); Loading
diffdeleted 100644 → 0 +0 −25 Original line number Diff line number Diff line diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index fc43882..832dc91 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -67,6 +67,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.NoSuchElementException; /** * This class represents an accessibility client - either an AccessibilityService or a UiAutomation. @@ -978,7 +979,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ /* ignore */ } if (mService != null) { - mService.unlinkToDeath(this, 0); + try { + mService.unlinkToDeath(this, 0); + }catch(NoSuchElementException e) { + Slog.e(LOG_TAG, "Failed unregistering death link"); + } mService = null; }
services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +96 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,8 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.database.ContentObserver; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.IFingerprintService; Loading Loading @@ -194,6 +196,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR); private final Rect mTempRect = new Rect(); private final Rect mTempRect1 = new Rect(); private final PackageManager mPackageManager; private final PowerManager mPowerManager; Loading Loading @@ -251,6 +256,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub //TODO: Remove this hack private boolean mInitialized; private Point mTempPoint = new Point(); private boolean mIsAccessibilityButtonShown; private AccessibilityUserState getCurrentUserStateLocked() { Loading Loading @@ -1085,6 +1091,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return motionEventInjector; } /** * Gets a point within the accessibility focused node where we can send down * and up events to perform a click. * * @param outPoint The click point to populate. * @return Whether accessibility a click point was found and set. */ // TODO: (multi-display) Make sure this works for multiple displays. public boolean getAccessibilityFocusClickPointInScreen(Point outPoint) { return getInteractionBridge().getAccessibilityFocusClickPointInScreenNotLocked(outPoint); } /** * Perform an accessibility action on the view that currently has accessibility focus. * Has no effect if no item has accessibility focus, if the item with accessibility Loading @@ -1099,6 +1117,32 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return getInteractionBridge().performActionOnAccessibilityFocusedItemNotLocked(action); } /** * Returns true if accessibility focus is confined to the active window. */ public boolean accessibilityFocusOnlyInActiveWindow() { synchronized (mLock) { return mA11yWindowManager.isTrackingWindowsLocked(); } } /** * Gets the bounds of a window. * * @param outBounds The output to which to write the bounds. */ boolean getWindowBounds(int windowId, Rect outBounds) { IBinder token; synchronized (mLock) { token = getWindowToken(windowId, mCurrentUserId); } mWindowManagerService.getWindowFrame(token, outBounds); if (!outBounds.isEmpty()) { return true; } return false; } public int getActiveWindowId() { return mA11yWindowManager.getActiveWindowId(mCurrentUserId); } Loading Loading @@ -1877,9 +1921,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub for (int i = 0; !observingWindows && (i < boundServiceCount); i++) { AccessibilityServiceConnection boundService = boundServices.get(i); if (boundService.canRetrieveInteractiveWindowsLocked()) { userState.setAccessibilityFocusOnlyInActiveWindow(false); observingWindows = true; } } userState.setAccessibilityFocusOnlyInActiveWindow(true); // Gets all valid displays and start tracking windows of each display if there is at least // one bound service that can retrieve window content. Loading Loading @@ -2996,6 +3042,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY); } /** * Gets a point within the accessibility focused node where we can send down and up events * to perform a click. * * @param outPoint The click point to populate. * @return Whether accessibility a click point was found and set. */ // TODO: (multi-display) Make sure this works for multiple displays. boolean getAccessibilityFocusClickPointInScreen(Point outPoint) { return getInteractionBridge() .getAccessibilityFocusClickPointInScreenNotLocked(outPoint); } /** * Perform an accessibility action on the view that currently has accessibility focus. * Has no effect if no item has accessibility focus, if the item with accessibility Loading @@ -3014,6 +3073,43 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return focus.performAction(action.getId()); } public boolean getAccessibilityFocusClickPointInScreenNotLocked(Point outPoint) { AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked(); if (focus == null) { return false; } synchronized (mLock) { Rect boundsInScreen = mTempRect; focus.getBoundsInScreen(boundsInScreen); // Apply magnification if needed. MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId()); if (spec != null && !spec.isNop()) { boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY); boundsInScreen.scale(1 / spec.scale); } // Clip to the window bounds. Rect windowBounds = mTempRect1; getWindowBounds(focus.getWindowId(), windowBounds); if (!boundsInScreen.intersect(windowBounds)) { return false; } // Clip to the screen bounds. Point screenSize = mTempPoint; mDefaultDisplay.getRealSize(screenSize); if (!boundsInScreen.intersect(0, 0, screenSize.x, screenSize.y)) { return false; } outPoint.set(boundsInScreen.centerX(), boundsInScreen.centerY()); } return true; } private AccessibilityNodeInfo getAccessibilityFocusNotLocked() { final int focusedWindowId; synchronized (mLock) { Loading