ExploreByTouchHelper.java revision 3236f136c34c3db9b05c69cdbaccdc0fa6203a1b
16eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette/* 26eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Copyright (C) 2013 The Android Open Source Project 36eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 46eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License"); 56eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * you may not use this file except in compliance with the License. 66eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * You may obtain a copy of the License at 76eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 86eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * http://www.apache.org/licenses/LICENSE-2.0 96eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Unless required by applicable law or agreed to in writing, software 116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS, 126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * See the License for the specific language governing permissions and 146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * limitations under the License. 156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverettepackage android.support.v4.widget; 186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.content.Context; 206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.graphics.Rect; 216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.os.Bundle; 22049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viveretteimport android.support.annotation.NonNull; 23049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viveretteimport android.support.annotation.Nullable; 24e69072d0197b6360882acd76d4f0271727cfe5bcAlan Viveretteimport android.support.v4.util.SparseArrayCompat; 256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.AccessibilityDelegateCompat; 266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.ViewCompat; 277f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viveretteimport android.support.v4.view.ViewCompat.FocusDirection; 287f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viveretteimport android.support.v4.view.ViewCompat.FocusRealDirection; 296eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.ViewParentCompat; 306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.accessibility.AccessibilityEventCompat; 316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.accessibility.AccessibilityManagerCompat; 326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 336eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.accessibility.AccessibilityNodeProviderCompat; 346eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.support.v4.view.accessibility.AccessibilityRecordCompat; 35049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viveretteimport android.view.KeyEvent; 366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.view.MotionEvent; 376eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.view.View; 386eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.view.ViewParent; 396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.view.accessibility.AccessibilityEvent; 406eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport android.view.accessibility.AccessibilityManager; 4114d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikasimport android.view.accessibility.AccessibilityRecord; 426eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 43049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viveretteimport java.util.ArrayList; 446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viveretteimport java.util.List; 456eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 466eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette/** 476eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * ExploreByTouchHelper is a utility class for implementing accessibility 486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * support in custom {@link View}s that represent a collection of View-like 496eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * logical items. It extends {@link AccessibilityNodeProviderCompat} and 506eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * simplifies many aspects of providing information to accessibility services 5167f879143b4b6d4bb07a90437df66174791781e8Zach Kuznia * and managing accessibility focus. 526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 5312d8b3f36c7b503221185b44791822e59e690966Alan Viverette * Clients should override abstract methods on this class and attach it to the 5412d8b3f36c7b503221185b44791822e59e690966Alan Viverette * host view using {@link ViewCompat#setAccessibilityDelegate}: 55049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <p> 566eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <pre> 57049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * class MyCustomView extends View { 58049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * private MyVirtualViewHelper mVirtualViewHelper; 59049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 60049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * public MyCustomView(Context context, ...) { 61049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * ... 62049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * mVirtualViewHelper = new MyVirtualViewHelper(this); 63049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * ViewCompat.setAccessibilityDelegate(this, mVirtualViewHelper); 64049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * } 65049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 66049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @Override 67049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * public boolean dispatchHoverEvent(MotionEvent event) { 68049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * return mHelper.dispatchHoverEvent(this, event) 69049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * || super.dispatchHoverEvent(event); 70049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * } 71049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 72049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @Override 73049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * public boolean dispatchKeyEvent(KeyEvent event) { 74049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * return mHelper.dispatchKeyEvent(event) 75049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * || super.dispatchKeyEvent(event); 76049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * } 77049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 78049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @Override 79049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * public boolean onFocusChanged(boolean gainFocus, int direction, 80049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Rect previouslyFocusedRect) { 81049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 82049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * mHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 83049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * } 84049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * } 8512d8b3f36c7b503221185b44791822e59e690966Alan Viverette * mAccessHelper = new MyExploreByTouchHelper(someView); 866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * ViewCompat.setAccessibilityDelegate(someView, mAccessHelper); 876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * </pre> 886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverettepublic abstract class ExploreByTouchHelper extends AccessibilityDelegateCompat { 906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** Virtual node identifier value for invalid nodes. */ 916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette public static final int INVALID_ID = Integer.MIN_VALUE; 926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 9312d8b3f36c7b503221185b44791822e59e690966Alan Viverette /** Virtual node identifier value for the host view's node. */ 9412d8b3f36c7b503221185b44791822e59e690966Alan Viverette public static final int HOST_ID = View.NO_ID; 9512d8b3f36c7b503221185b44791822e59e690966Alan Viverette 966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** Default class name used for virtual views. */ 97049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private static final String DEFAULT_CLASS_NAME = "android.view.View"; 986eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 99cc35612e283564c245408da4bb9264f38a02487aAlan Viverette /** Default bounds used to determine if the client didn't set any. */ 100cc35612e283564c245408da4bb9264f38a02487aAlan Viverette private static final Rect INVALID_PARENT_BOUNDS = new Rect( 101cc35612e283564c245408da4bb9264f38a02487aAlan Viverette Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); 102cc35612e283564c245408da4bb9264f38a02487aAlan Viverette 1036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Temporary, reusable data structures. 1046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private final Rect mTempScreenRect = new Rect(); 1056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private final Rect mTempParentRect = new Rect(); 1066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private final Rect mTempVisibleRect = new Rect(); 1076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private final int[] mTempGlobalRect = new int[2]; 1086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 1096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** System accessibility manager, used to check state and send events. */ 1106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private final AccessibilityManager mManager; 1116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 1126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** View whose internal structure is exposed through this helper. */ 113049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private final View mHost; 1146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 115049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** Virtual node provider used to expose logical structure to services. */ 116049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private MyNodeProvider mNodeProvider; 1176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 118049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** Identifier for the virtual view that holds accessibility focus. */ 119049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private int mAccessibilityFocusedVirtualViewId = INVALID_ID; 1206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 121049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** Identifier for the virtual view that holds keyboard focus. */ 122049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private int mKeyboardFocusedVirtualViewId = INVALID_ID; 123049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 124049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** Identifier for the virtual view that is currently hovered. */ 1256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private int mHoveredVirtualViewId = INVALID_ID; 1266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 1276eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 128049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Constructs a new helper that can expose a virtual view hierarchy for the 129049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * specified host view. 1306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 131049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param host view whose virtual view hierarchy is exposed by this helper 1326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 133049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public ExploreByTouchHelper(View host) { 134049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (host == null) { 1356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette throw new IllegalArgumentException("View may not be null"); 1366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 1376eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 138049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette mHost = host; 139049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 140049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final Context context = host.getContext(); 1416eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette mManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); 142049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 143049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // Host view must be focusable so that we can delegate to virtual 144049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // views. 145049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette host.setFocusable(true); 1467f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette if (ViewCompat.getImportantForAccessibility(host) 1477f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 1487f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette ViewCompat.setImportantForAccessibility( 1497f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette host, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); 150049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 1516eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 1526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 1536eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette @Override 1546eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) { 1556eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if (mNodeProvider == null) { 156049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette mNodeProvider = new MyNodeProvider(); 1576eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 1586eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return mNodeProvider; 1596eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 1606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 1616eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 162049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Delegates hover events from the host view. 163049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <p> 1646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Dispatches hover {@link MotionEvent}s to the virtual view hierarchy when 1656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * the Explore by Touch feature is enabled. 1666eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 167049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * This method should be called by overriding the host view's 168049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link View#dispatchHoverEvent(MotionEvent)} method: 1696eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <pre>@Override 1706eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * public boolean dispatchHoverEvent(MotionEvent event) { 171049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * return mHelper.dispatchHoverEvent(this, event) 172049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * || super.dispatchHoverEvent(event); 1736eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * } 1746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * </pre> 1756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 1766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @param event The hover event to dispatch to the virtual view hierarchy. 1776eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @return Whether the hover event was handled. 1786eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 179049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public final boolean dispatchHoverEvent(@NonNull MotionEvent event) { 1806eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if (!mManager.isEnabled() 1816eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette || !AccessibilityManagerCompat.isTouchExplorationEnabled(mManager)) { 1826eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return false; 1836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 1846eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 1856eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette switch (event.getAction()) { 1866ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas case MotionEvent.ACTION_HOVER_MOVE: 1876ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas case MotionEvent.ACTION_HOVER_ENTER: 1886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette final int virtualViewId = getVirtualViewAt(event.getX(), event.getY()); 1896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette updateHoveredVirtualView(virtualViewId); 1906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return (virtualViewId != INVALID_ID); 1916ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas case MotionEvent.ACTION_HOVER_EXIT: 192049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (mAccessibilityFocusedVirtualViewId != INVALID_ID) { 1936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette updateHoveredVirtualView(INVALID_ID); 1946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return true; 1956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 1966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return false; 1976eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette default: 1986eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return false; 1996eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 2006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 2016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 2026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 203049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Delegates key events from the host view. 204049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <p> 205049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * This method should be called by overriding the host view's 206049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link View#dispatchKeyEvent(KeyEvent)} method: 207049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <pre>@Override 208049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * public boolean dispatchKeyEvent(KeyEvent event) { 209049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * return mHelper.dispatchKeyEvent(event) 210049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * || super.dispatchKeyEvent(event); 211049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * } 212049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * </pre> 213049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 214049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public final boolean dispatchKeyEvent(@NonNull KeyEvent event) { 215049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette boolean handled = false; 216049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 217049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final int action = event.getAction(); 218049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (action != KeyEvent.ACTION_UP) { 219049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final int keyCode = event.getKeyCode(); 220049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette switch (keyCode) { 221049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case KeyEvent.KEYCODE_DPAD_LEFT: 222049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case KeyEvent.KEYCODE_DPAD_UP: 223049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case KeyEvent.KEYCODE_DPAD_RIGHT: 224049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case KeyEvent.KEYCODE_DPAD_DOWN: 2259c3a43f2608c2e7fceea46e4b500d68ce1c619ffAurimas Liutikas if (event.hasNoModifiers()) { 226049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final int direction = keyToDirection(keyCode); 227049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final int count = 1 + event.getRepeatCount(); 228049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette for (int i = 0; i < count; i++) { 229049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (moveFocus(direction, null)) { 230049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette handled = true; 231049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } else { 232049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette break; 233049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 234049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 235049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 236049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette break; 237049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case KeyEvent.KEYCODE_DPAD_CENTER: 238049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case KeyEvent.KEYCODE_ENTER: 2399c3a43f2608c2e7fceea46e4b500d68ce1c619ffAurimas Liutikas if (event.hasNoModifiers()) { 240049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (event.getRepeatCount() == 0) { 241049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette clickKeyboardFocusedVirtualView(); 242049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette handled = true; 243049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 244049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 245049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette break; 246049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case KeyEvent.KEYCODE_TAB: 2479c3a43f2608c2e7fceea46e4b500d68ce1c619ffAurimas Liutikas if (event.hasNoModifiers()) { 248049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette handled = moveFocus(View.FOCUS_FORWARD, null); 2499c3a43f2608c2e7fceea46e4b500d68ce1c619ffAurimas Liutikas } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { 250049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette handled = moveFocus(View.FOCUS_BACKWARD, null); 251049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 252049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette break; 253049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 254049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 255049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 256049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return handled; 257049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 258049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 259049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 260049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Delegates focus changes from the host view. 261049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <p> 262049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * This method should be called by overriding the host view's 263049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link View#onFocusChanged(boolean, int, Rect)} method: 264049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <pre>@Override 265049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * public boolean onFocusChanged(boolean gainFocus, int direction, 266049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Rect previouslyFocusedRect) { 267049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 268049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * mHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 269049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * } 270049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * </pre> 271049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 272049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public final void onFocusChanged(boolean gainFocus, int direction, 273049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette @Nullable Rect previouslyFocusedRect) { 274049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (mKeyboardFocusedVirtualViewId != INVALID_ID) { 275049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette clearKeyboardFocusForVirtualView(mKeyboardFocusedVirtualViewId); 276049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 277049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 278049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (gainFocus) { 279049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette moveFocus(direction, previouslyFocusedRect); 280049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 281049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 282049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 283049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 284049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return the identifier of the virtual view that has accessibility focus 285049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * or {@link #INVALID_ID} if no virtual view has accessibility 286049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * focus 287049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 288049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public final int getAccessibilityFocusedVirtualViewId() { 289049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return mAccessibilityFocusedVirtualViewId; 290049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 291049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 292049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 293049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return the identifier of the virtual view that has keyboard focus 294049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * or {@link #INVALID_ID} if no virtual view has keyboard focus 295049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 296049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public final int getKeyboardFocusedVirtualViewId() { 297049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return mKeyboardFocusedVirtualViewId; 298049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 299049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 300049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 301049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Maps key event codes to focus directions. 302049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 303049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param keyCode the key event code 304049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return the corresponding focus direction 305049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 306049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette @FocusRealDirection 307049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private static int keyToDirection(int keyCode) { 308049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette switch (keyCode) { 309049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case KeyEvent.KEYCODE_DPAD_LEFT: 310049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return View.FOCUS_LEFT; 311049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case KeyEvent.KEYCODE_DPAD_UP: 312049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return View.FOCUS_UP; 313049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case KeyEvent.KEYCODE_DPAD_RIGHT: 314049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return View.FOCUS_RIGHT; 315049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette default: 316049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return View.FOCUS_DOWN; 317049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 318049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 319049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 320049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 321049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Obtains the bounds for the specified virtual view. 322049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 323049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the identifier of the virtual view 324049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param outBounds the rect to populate with virtual view bounds 325049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 326049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private void getBoundsInParent(int virtualViewId, Rect outBounds) { 327049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final AccessibilityNodeInfoCompat node = obtainAccessibilityNodeInfo(virtualViewId); 328049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette node.getBoundsInParent(outBounds); 329049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 330049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 331049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 332049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Adapts AccessibilityNodeInfoCompat for obtaining bounds. 333049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 334049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private static final FocusStrategy.BoundsAdapter<AccessibilityNodeInfoCompat> NODE_ADAPTER = 335049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette new FocusStrategy.BoundsAdapter<AccessibilityNodeInfoCompat>() { 336049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette @Override 337049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public void obtainBounds(AccessibilityNodeInfoCompat node, Rect outBounds) { 338049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette node.getBoundsInParent(outBounds); 339049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 340049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette }; 341049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 342049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 343e69072d0197b6360882acd76d4f0271727cfe5bcAlan Viverette * Adapts SparseArrayCompat for iterating through values. 344049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 345e69072d0197b6360882acd76d4f0271727cfe5bcAlan Viverette private static final FocusStrategy.CollectionAdapter<SparseArrayCompat< 346e69072d0197b6360882acd76d4f0271727cfe5bcAlan Viverette AccessibilityNodeInfoCompat>, AccessibilityNodeInfoCompat> SPARSE_VALUES_ADAPTER = 347e69072d0197b6360882acd76d4f0271727cfe5bcAlan Viverette new FocusStrategy.CollectionAdapter<SparseArrayCompat< 348e69072d0197b6360882acd76d4f0271727cfe5bcAlan Viverette AccessibilityNodeInfoCompat>, AccessibilityNodeInfoCompat>() { 349049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette @Override 350049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public AccessibilityNodeInfoCompat get( 351e69072d0197b6360882acd76d4f0271727cfe5bcAlan Viverette SparseArrayCompat<AccessibilityNodeInfoCompat> collection, int index) { 352049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return collection.valueAt(index); 353049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 354049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 355049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette @Override 356e69072d0197b6360882acd76d4f0271727cfe5bcAlan Viverette public int size(SparseArrayCompat<AccessibilityNodeInfoCompat> collection) { 357049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return collection.size(); 358049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 359049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette }; 360049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 361049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 362049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Attempts to move keyboard focus in the specified direction. 363049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 364049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param direction the direction in which to move keyboard focus 365049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param previouslyFocusedRect the bounds of the previously focused item, 366049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * or {@code null} if not available 367049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return {@code true} if keyboard focus moved to a virtual view managed 368049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * by this helper, or {@code false} otherwise 369049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 370049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private boolean moveFocus(@FocusDirection int direction, @Nullable Rect previouslyFocusedRect) { 3711b327048091c92b5f46981792930aba4ab122c30Alan Viverette final SparseArrayCompat<AccessibilityNodeInfoCompat> allNodes = getAllNodes(); 3721b327048091c92b5f46981792930aba4ab122c30Alan Viverette 373049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final int focusedNodeId = mKeyboardFocusedVirtualViewId; 374049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final AccessibilityNodeInfoCompat focusedNode = 3751b327048091c92b5f46981792930aba4ab122c30Alan Viverette focusedNodeId == INVALID_ID ? null : allNodes.get(focusedNodeId); 376049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 377049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final AccessibilityNodeInfoCompat nextFocusedNode; 378049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette switch (direction) { 379049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case View.FOCUS_FORWARD: 380049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case View.FOCUS_BACKWARD: 381049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final boolean isLayoutRtl = 382049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette ViewCompat.getLayoutDirection(mHost) == ViewCompat.LAYOUT_DIRECTION_RTL; 3831b327048091c92b5f46981792930aba4ab122c30Alan Viverette nextFocusedNode = FocusStrategy.findNextFocusInRelativeDirection(allNodes, 384049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette SPARSE_VALUES_ADAPTER, NODE_ADAPTER, focusedNode, direction, isLayoutRtl, 385049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette false); 386049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette break; 387049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case View.FOCUS_LEFT: 388049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case View.FOCUS_UP: 389049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case View.FOCUS_RIGHT: 390049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case View.FOCUS_DOWN: 391049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final Rect selectedRect = new Rect(); 392049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (mKeyboardFocusedVirtualViewId != INVALID_ID) { 393049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // Focus is moving from a virtual view within the host. 394049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette getBoundsInParent(mKeyboardFocusedVirtualViewId, selectedRect); 395049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } else if (previouslyFocusedRect != null) { 396049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // Focus is moving from a real view outside the host. 397049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette selectedRect.set(previouslyFocusedRect); 398049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } else { 399049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // Focus is moving from... somewhere? Make a guess. 400049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // Usually this happens when another view was too lazy 401049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // to pass the previously focused rect (ex. ScrollView 402049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // when moving UP or DOWN). 403049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette guessPreviouslyFocusedRect(mHost, direction, selectedRect); 404049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 4051b327048091c92b5f46981792930aba4ab122c30Alan Viverette nextFocusedNode = FocusStrategy.findNextFocusInAbsoluteDirection(allNodes, 406049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette SPARSE_VALUES_ADAPTER, NODE_ADAPTER, focusedNode, selectedRect, direction); 407049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette break; 408049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette default: 409049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette throw new IllegalArgumentException("direction must be one of " 410049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette + "{FOCUS_FORWARD, FOCUS_BACKWARD, FOCUS_UP, FOCUS_DOWN, " 411049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette + "FOCUS_LEFT, FOCUS_RIGHT}."); 412049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 413049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 414049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final int nextFocusedNodeId; 415049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (nextFocusedNode == null) { 416049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette nextFocusedNodeId = INVALID_ID; 417049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } else { 4181b327048091c92b5f46981792930aba4ab122c30Alan Viverette final int index = allNodes.indexOfValue(nextFocusedNode); 4191b327048091c92b5f46981792930aba4ab122c30Alan Viverette nextFocusedNodeId = allNodes.keyAt(index); 420049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 421049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 422049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return requestKeyboardFocusForVirtualView(nextFocusedNodeId); 423049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 424049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 4251b327048091c92b5f46981792930aba4ab122c30Alan Viverette private SparseArrayCompat<AccessibilityNodeInfoCompat> getAllNodes() { 4261b327048091c92b5f46981792930aba4ab122c30Alan Viverette final List<Integer> virtualViewIds = new ArrayList<>(); 4271b327048091c92b5f46981792930aba4ab122c30Alan Viverette getVisibleVirtualViews(virtualViewIds); 4281b327048091c92b5f46981792930aba4ab122c30Alan Viverette 4291b327048091c92b5f46981792930aba4ab122c30Alan Viverette final SparseArrayCompat<AccessibilityNodeInfoCompat> allNodes = new SparseArrayCompat<>(); 4301b327048091c92b5f46981792930aba4ab122c30Alan Viverette for (int virtualViewId = 0; virtualViewId < virtualViewIds.size(); virtualViewId++) { 4311b327048091c92b5f46981792930aba4ab122c30Alan Viverette final AccessibilityNodeInfoCompat virtualView = createNodeForChild(virtualViewId); 4321b327048091c92b5f46981792930aba4ab122c30Alan Viverette allNodes.put(virtualViewId, virtualView); 4331b327048091c92b5f46981792930aba4ab122c30Alan Viverette } 4341b327048091c92b5f46981792930aba4ab122c30Alan Viverette 4351b327048091c92b5f46981792930aba4ab122c30Alan Viverette return allNodes; 4361b327048091c92b5f46981792930aba4ab122c30Alan Viverette } 4371b327048091c92b5f46981792930aba4ab122c30Alan Viverette 438049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 439049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Obtains a best guess for the previously focused rect for keyboard focus 440049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * moving in the specified direction. 441049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 442049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param host the view into which focus is moving 443049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param direction the absolute direction in which focus is moving 444049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param outBounds the rect to populate with the best-guess bounds for the 445049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * previous focus rect 446049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 447049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private static Rect guessPreviouslyFocusedRect(@NonNull View host, 448049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette @FocusRealDirection int direction, @NonNull Rect outBounds) { 449049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final int w = host.getWidth(); 450049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final int h = host.getHeight(); 451049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 452049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette switch (direction) { 453049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case View.FOCUS_LEFT: 454049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette outBounds.set(w, 0, w, h); 455049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette break; 456049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case View.FOCUS_UP: 457049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette outBounds.set(0, h, w, h); 458049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette break; 459049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case View.FOCUS_RIGHT: 460049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette outBounds.set(-1, 0, -1, h); 461049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette break; 462049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case View.FOCUS_DOWN: 463049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette outBounds.set(0, -1, w, -1); 464049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette break; 465049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette default: 466049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette throw new IllegalArgumentException("direction must be one of " 467049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}."); 468049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 469049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 470049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return outBounds; 471049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 472049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 473049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 474049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Performs a click action on the keyboard focused virtual view, if any. 475049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 476049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return {@code true} if the click action was performed successfully or 477049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@code false} otherwise 478049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 479049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private boolean clickKeyboardFocusedVirtualView() { 480049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return mKeyboardFocusedVirtualViewId != INVALID_ID && onPerformActionForVirtualView( 481049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette mKeyboardFocusedVirtualViewId, AccessibilityNodeInfoCompat.ACTION_CLICK, null); 482049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 483049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 484049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 4856eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Populates an event of the specified type with information about an item 4866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * and attempts to send it up through the view hierarchy. 4876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 4886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * You should call this method after performing a user action that normally 4896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * fires an accessibility event, such as clicking on an item. 490049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <p> 4916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <pre>public void performItemClick(T item) { 4926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * ... 4936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * sendEventForVirtualViewId(item.id, AccessibilityEvent.TYPE_VIEW_CLICKED); 4946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * } 4956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * </pre> 4966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 497049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the identifier of the virtual view for which to 498049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * send an event 499049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param eventType the type of event to send 500049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return {@code true} if the event was sent successfully, {@code false} 501049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * otherwise 5026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 503049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public final boolean sendEventForVirtualView(int virtualViewId, int eventType) { 5046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if ((virtualViewId == INVALID_ID) || !mManager.isEnabled()) { 5056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return false; 5066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 5076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 508049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final ViewParent parent = mHost.getParent(); 5096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if (parent == null) { 5106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return false; 5116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 5126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 5136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette final AccessibilityEvent event = createEvent(virtualViewId, eventType); 514049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return ViewParentCompat.requestSendAccessibilityEvent(parent, mHost, event); 5156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 5166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 5176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 5186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Notifies the accessibility framework that the properties of the parent 5196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * view have changed. 5206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 521049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * You <strong>must</strong> call this method after adding or removing 522049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * items from the parent view. 5236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 524049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public final void invalidateRoot() { 525722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette invalidateVirtualView(HOST_ID, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE); 5266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 5276eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 5286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 5296eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Notifies the accessibility framework that the properties of a particular 5306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * item have changed. 5316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 532049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * You <strong>must</strong> call this method after changing any of the 533049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * properties set in 534049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}. 5356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 536049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the virtual view id to invalidate, or 537049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link #HOST_ID} to invalidate the root view 538722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * @see #invalidateVirtualView(int, int) 5396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 540049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public final void invalidateVirtualView(int virtualViewId) { 541722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette invalidateVirtualView(virtualViewId, 542722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED); 543722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette } 544722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette 545722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette /** 546722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * Notifies the accessibility framework that the properties of a particular 547722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * item have changed. 548722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * <p> 549049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * You <strong>must</strong> call this method after changing any of the 550049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * properties set in 551049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}. 552722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * 553049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the virtual view id to invalidate, or 554049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link #HOST_ID} to invalidate the root view 555049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param changeTypes the bit mask of change types. May be {@code 0} for the 556722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * default (undefined) change type or one or more of: 557722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * <ul> 558722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION} 559722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_SUBTREE} 560722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_TEXT} 561722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_UNDEFINED} 562722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette * </ul> 563722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette */ 564049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public final void invalidateVirtualView(int virtualViewId, int changeTypes) { 565722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette if (virtualViewId != INVALID_ID && mManager.isEnabled()) { 566049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final ViewParent parent = mHost.getParent(); 567722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette if (parent != null) { 568049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // Send events up the hierarchy so they can be coalesced. 569722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette final AccessibilityEvent event = createEvent(virtualViewId, 570722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED); 571722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette AccessibilityEventCompat.setContentChangeTypes(event, changeTypes); 572049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette ViewParentCompat.requestSendAccessibilityEvent(parent, mHost, event); 573722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette } 574722514df962c62eee53f82df927b8969bc5ceb95Alan Viverette } 5756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 5766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 5776eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 578049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Returns the virtual view ID for the currently accessibility focused 579049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * item. 580148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette * 581049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return the identifier of the virtual view that has accessibility focus 582049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * or {@link #INVALID_ID} if no virtual view has accessibility 583049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * focus 584049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @deprecated Use {@link #getAccessibilityFocusedVirtualViewId()}. 585148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette */ 586049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette @Deprecated 587148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette public int getFocusedVirtualView() { 588049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return getAccessibilityFocusedVirtualViewId(); 589049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 590049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 591049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 592049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Called when the focus state of a virtual view changes. 593049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 594049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the virtual view identifier 595049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param hasFocus {@code true} if the view has focus, {@code false} 596049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * otherwise 597049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 598049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette protected void onVirtualViewKeyboardFocusChanged(int virtualViewId, boolean hasFocus) { 599049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // Stub method. 600148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette } 601148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette 602148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette /** 6036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Sets the currently hovered item, sending hover accessibility events as 6046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * necessary to maintain the correct state. 6056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 606049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the virtual view id for the item currently being 607049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * hovered, or {@link #INVALID_ID} if no item is 608049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * hovered within the parent view 6096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 6106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private void updateHoveredVirtualView(int virtualViewId) { 6116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if (mHoveredVirtualViewId == virtualViewId) { 6126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return; 6136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 6146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 6156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette final int previousVirtualViewId = mHoveredVirtualViewId; 6166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette mHoveredVirtualViewId = virtualViewId; 6176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 6186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Stay consistent with framework behavior by sending ENTER/EXIT pairs 6196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // in reverse order. This is accurate as of API 18. 62014d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); 6216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette sendEventForVirtualView( 62214d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas previousVirtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); 6236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 6246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 6256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 6266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Constructs and returns an {@link AccessibilityEvent} for the specified 62712d8b3f36c7b503221185b44791822e59e690966Alan Viverette * virtual view id, which includes the host view ({@link #HOST_ID}). 6286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 629049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the virtual view id for the item for which to 630049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * construct an event 631049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param eventType the type of event to construct 632049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return an {@link AccessibilityEvent} populated with information about 633049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * the specified item 6346eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 6356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private AccessibilityEvent createEvent(int virtualViewId, int eventType) { 6366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette switch (virtualViewId) { 63712d8b3f36c7b503221185b44791822e59e690966Alan Viverette case HOST_ID: 6386eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return createEventForHost(eventType); 6396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette default: 6406eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return createEventForChild(virtualViewId, eventType); 6416eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 6426eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 6436eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 6446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 6456eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Constructs and returns an {@link AccessibilityEvent} for the host node. 6466eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 647049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param eventType the type of event to construct 648049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return an {@link AccessibilityEvent} populated with information about 649049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * the specified item 6506eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 6516eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private AccessibilityEvent createEventForHost(int eventType) { 6526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); 653049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette ViewCompat.onInitializeAccessibilityEvent(mHost, event); 6547f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette return event; 6557f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette } 6567f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette 6577f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette @Override 6587f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { 6597f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette super.onInitializeAccessibilityEvent(host, event); 660fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette 661fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette // Allow the client to populate the event. 662fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette onPopulateEventForHost(event); 6636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 6646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 6656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 6666eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Constructs and returns an {@link AccessibilityEvent} populated with 6676eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * information about the specified item. 6686eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 669049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the virtual view id for the item for which to 670049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * construct an event 671049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param eventType the type of event to construct 672049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return an {@link AccessibilityEvent} populated with information about 673049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * the specified item 6746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 6756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) { 6766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); 677049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final AccessibilityNodeInfoCompat node = obtainAccessibilityNodeInfo(virtualViewId); 678049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 679049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // Allow the client to override these properties, 68014d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.getText().add(node.getText()); 68114d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setContentDescription(node.getContentDescription()); 68214d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setScrollable(node.isScrollable()); 68314d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setPassword(node.isPassword()); 68414d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setEnabled(node.isEnabled()); 68514d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setChecked(node.isChecked()); 6866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 6876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Allow the client to populate the event. 6886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette onPopulateEventForVirtualView(virtualViewId, event); 6896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 6906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Make sure the developer is following the rules. 6916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if (event.getText().isEmpty() && (event.getContentDescription() == null)) { 6926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette throw new RuntimeException("Callbacks must add text or a content description in " 6936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette + "populateEventForVirtualViewId()"); 6946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 6956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 6966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Don't allow the client to override these properties. 69714d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setClassName(node.getClassName()); 69814d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas AccessibilityRecordCompat.setSource(event, mHost, virtualViewId); 699049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette event.setPackageName(mHost.getContext().getPackageName()); 7006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 7016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return event; 7026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 7036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 7046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 705049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Obtains a populated {@link AccessibilityNodeInfoCompat} for the 706049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * virtual view with the specified identifier. 707049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <p> 708049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * This method may be called with identifier {@link #HOST_ID} to obtain a 709049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * node for the host view. 7106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 711049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the identifier of the virtual view for which to 712049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * construct a node 713049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return an {@link AccessibilityNodeInfoCompat} populated with information 714049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * about the specified item 7156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 716049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette @NonNull 717540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas AccessibilityNodeInfoCompat obtainAccessibilityNodeInfo(int virtualViewId) { 7181b327048091c92b5f46981792930aba4ab122c30Alan Viverette if (virtualViewId == HOST_ID) { 7191b327048091c92b5f46981792930aba4ab122c30Alan Viverette return createNodeForHost(); 7206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 721049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 7221b327048091c92b5f46981792930aba4ab122c30Alan Viverette return createNodeForChild(virtualViewId); 7236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 7246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 7256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 7266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the 7276eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * host view populated with its virtual descendants. 7286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 729049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return an {@link AccessibilityNodeInfoCompat} for the parent node 7306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 7311fe6cc2f16121bc57f6a89aaa5502e47ab3e8fe9Alan Viverette @NonNull 7326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private AccessibilityNodeInfoCompat createNodeForHost() { 7337f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(mHost); 7347f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette ViewCompat.onInitializeAccessibilityNodeInfo(mHost, info); 73512d8b3f36c7b503221185b44791822e59e690966Alan Viverette 7366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Add the virtual descendants. 737049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final ArrayList<Integer> virtualViewIds = new ArrayList<>(); 7386eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette getVisibleVirtualViews(virtualViewIds); 7397f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette 7407f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette final int realNodeCount = info.getChildCount(); 741fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette if (realNodeCount > 0 && virtualViewIds.size() > 0) { 742fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette throw new RuntimeException("Views cannot have both real and virtual children"); 743fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette } 7446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 745049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette for (int i = 0, count = virtualViewIds.size(); i < count; i++) { 7467f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette info.addChild(mHost, virtualViewIds.get(i)); 7476eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 7486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 7497f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette return info; 7507f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette } 7517f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette 7527f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette @Override 7537f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { 7547f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette super.onInitializeAccessibilityNodeInfo(host, info); 7557f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette 7567f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette // Allow the client to populate the host node. 7577f62b32cd8fe966a5b1eaa850fb6595347a2564bAlan Viverette onPopulateNodeForHost(info); 7586eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 7596eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 7606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 7616eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the 7626eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * specified item. Automatically manages accessibility focus actions. 7636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 7646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Allows the implementing class to specify most node properties, but 7656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * overrides the following: 7666eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <ul> 7676eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>{@link AccessibilityNodeInfoCompat#setPackageName} 7686eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>{@link AccessibilityNodeInfoCompat#setClassName} 7696eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>{@link AccessibilityNodeInfoCompat#setParent(View)} 7706eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>{@link AccessibilityNodeInfoCompat#setSource(View, int)} 7716eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser} 7726eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)} 7736eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * </ul> 7746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 7756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Uses the bounds of the parent view and the parent-relative bounding 7766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * rectangle specified by 7776eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * {@link AccessibilityNodeInfoCompat#getBoundsInParent} to automatically 7786eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * update the following properties: 7796eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <ul> 7806eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser} 7816eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent} 7826eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * </ul> 7836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 784049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the virtual view id for item for which to construct 785049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * a node 786049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return an {@link AccessibilityNodeInfoCompat} for the specified item 7876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 7881fe6cc2f16121bc57f6a89aaa5502e47ab3e8fe9Alan Viverette @NonNull 7896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private AccessibilityNodeInfoCompat createNodeForChild(int virtualViewId) { 7906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain(); 7916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 7926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Ensure the client has good defaults. 7936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette node.setEnabled(true); 794049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette node.setFocusable(true); 7956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette node.setClassName(DEFAULT_CLASS_NAME); 796cc35612e283564c245408da4bb9264f38a02487aAlan Viverette node.setBoundsInParent(INVALID_PARENT_BOUNDS); 7970906dafe25770e872a72f8abd2c044b0faef86d7Alan Viverette node.setBoundsInScreen(INVALID_PARENT_BOUNDS); 79846f49c1900fc65a3e3bc0057e28161be6230e1daZach Kuznia node.setParent(mHost); 7996eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 8006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Allow the client to populate the node. 8016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette onPopulateNodeForVirtualView(virtualViewId, node); 8026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 8036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Make sure the developer is following the rules. 8046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if ((node.getText() == null) && (node.getContentDescription() == null)) { 8056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette throw new RuntimeException("Callbacks must add text or a content description in " 8066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette + "populateNodeForVirtualViewId()"); 8076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 8086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 8096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette node.getBoundsInParent(mTempParentRect); 810cc35612e283564c245408da4bb9264f38a02487aAlan Viverette if (mTempParentRect.equals(INVALID_PARENT_BOUNDS)) { 8116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette throw new RuntimeException("Callbacks must set parent bounds in " 8126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette + "populateNodeForVirtualViewId()"); 8136eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 8146eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 8156eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette final int actions = node.getActions(); 8166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if ((actions & AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS) != 0) { 8176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in " 8186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette + "populateNodeForVirtualViewId()"); 8196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 8206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if ((actions & AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) { 8216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in " 8226eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette + "populateNodeForVirtualViewId()"); 8236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 8246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 8256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Don't allow the client to override these properties. 826049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette node.setPackageName(mHost.getContext().getPackageName()); 827049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette node.setSource(mHost, virtualViewId); 8286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 8296eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Manage internal accessibility focus state. 830049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (mAccessibilityFocusedVirtualViewId == virtualViewId) { 8316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette node.setAccessibilityFocused(true); 8326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS); 8336eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } else { 8346eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette node.setAccessibilityFocused(false); 8356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette node.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS); 8366eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 8376eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 838049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // Manage internal keyboard focus state. 839049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette final boolean isFocused = mKeyboardFocusedVirtualViewId == virtualViewId; 840049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (isFocused) { 841049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS); 842049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } else if (node.isFocusable()) { 843049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette node.addAction(AccessibilityNodeInfoCompat.ACTION_FOCUS); 844049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 845049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette node.setFocused(isFocused); 846049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 8473b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia mHost.getLocationOnScreen(mTempGlobalRect); 8486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 8490906dafe25770e872a72f8abd2c044b0faef86d7Alan Viverette // If not explicitly specified, calculate screen-relative bounds and 8500906dafe25770e872a72f8abd2c044b0faef86d7Alan Viverette // offset for scroll position based on bounds in parent. 8510906dafe25770e872a72f8abd2c044b0faef86d7Alan Viverette node.getBoundsInScreen(mTempScreenRect); 8520906dafe25770e872a72f8abd2c044b0faef86d7Alan Viverette if (mTempScreenRect.equals(INVALID_PARENT_BOUNDS)) { 8530906dafe25770e872a72f8abd2c044b0faef86d7Alan Viverette node.getBoundsInParent(mTempScreenRect); 8543b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia 8553b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia // If there is a parent node, adjust bounds based on the parent node. 8563b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia if (node.mParentVirtualDescendantId != HOST_ID) { 8573b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia AccessibilityNodeInfoCompat parentNode = AccessibilityNodeInfoCompat.obtain(); 8583b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia // Walk up the node tree to adjust the screen rect. 8593b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia for (int virtualDescendantId = node.mParentVirtualDescendantId; 8603b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia virtualDescendantId != HOST_ID; 8613b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia virtualDescendantId = parentNode.mParentVirtualDescendantId) { 8623b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia // Reset the values in the parent node we'll be using. 8633b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia parentNode.setParent(mHost, HOST_ID); 8643b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia parentNode.setBoundsInParent(INVALID_PARENT_BOUNDS); 8653b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia // Adjust the bounds for the parent node. 8663b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia onPopulateNodeForVirtualView(virtualDescendantId, parentNode); 8673b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia parentNode.getBoundsInParent(mTempParentRect); 8683b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia mTempScreenRect.offset(mTempParentRect.left, mTempParentRect.top); 8693b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia } 8703b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia parentNode.recycle(); 8713b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia } 8723b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia // Adjust the rect for the host view's location. 8730906dafe25770e872a72f8abd2c044b0faef86d7Alan Viverette mTempScreenRect.offset(mTempGlobalRect[0] - mHost.getScrollX(), 8740906dafe25770e872a72f8abd2c044b0faef86d7Alan Viverette mTempGlobalRect[1] - mHost.getScrollY()); 8753b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia } 8763b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia 8773b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia if (mHost.getLocalVisibleRect(mTempVisibleRect)) { 8783b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia mTempVisibleRect.offset(mTempGlobalRect[0] - mHost.getScrollX(), 8793b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia mTempGlobalRect[1] - mHost.getScrollY()); 8803236f136c34c3db9b05c69cdbaccdc0fa6203a1bAurimas Liutikas final boolean intersects = mTempScreenRect.intersect(mTempVisibleRect); 8813236f136c34c3db9b05c69cdbaccdc0fa6203a1bAurimas Liutikas if (intersects) { 8823236f136c34c3db9b05c69cdbaccdc0fa6203a1bAurimas Liutikas node.setBoundsInScreen(mTempScreenRect); 8833b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia 8843236f136c34c3db9b05c69cdbaccdc0fa6203a1bAurimas Liutikas if (isVisibleToUser(mTempScreenRect)) { 8853236f136c34c3db9b05c69cdbaccdc0fa6203a1bAurimas Liutikas node.setVisibleToUser(true); 8863236f136c34c3db9b05c69cdbaccdc0fa6203a1bAurimas Liutikas } 8873b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia } 8880906dafe25770e872a72f8abd2c044b0faef86d7Alan Viverette } 8896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 8906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return node; 8916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 8926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 893540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas boolean performAction(int virtualViewId, int action, Bundle arguments) { 8946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette switch (virtualViewId) { 89512d8b3f36c7b503221185b44791822e59e690966Alan Viverette case HOST_ID: 8966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return performActionForHost(action, arguments); 8976eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette default: 8986eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return performActionForChild(virtualViewId, action, arguments); 8996eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 9026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private boolean performActionForHost(int action, Bundle arguments) { 903049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return ViewCompat.performAccessibilityAction(mHost, action, arguments); 9046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 9066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private boolean performActionForChild(int virtualViewId, int action, Bundle arguments) { 9076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette switch (action) { 9086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS: 909148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette return requestAccessibilityFocus(virtualViewId); 9106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS: 911148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette return clearAccessibilityFocus(virtualViewId); 912049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case AccessibilityNodeInfoCompat.ACTION_FOCUS: 913049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return requestKeyboardFocusForVirtualView(virtualViewId); 914049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette case AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS: 915049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return clearKeyboardFocusForVirtualView(virtualViewId); 9166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette default: 917049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return onPerformActionForVirtualView(virtualViewId, action, arguments); 9186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 9216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 9226eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Computes whether the specified {@link Rect} intersects with the visible 9236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * portion of its parent {@link View}. Modifies {@code localRect} to contain 9246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * only the visible portion. 9256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 926049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param localRect a rectangle in local (parent) coordinates 927049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return whether the specified {@link Rect} is visible on the screen 9286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 9293b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia private boolean isVisibleToUser(Rect localRect) { 9306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Missing or empty bounds mean this view is not visible. 9316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if ((localRect == null) || localRect.isEmpty()) { 9326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return false; 9336eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9346eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 9356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // Attached to invisible window means this view is not visible. 936049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (mHost.getWindowVisibility() != View.VISIBLE) { 9376eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return false; 9386eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 9406eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // An invisible predecessor means that this view is not visible. 941049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette ViewParent viewParent = mHost.getParent(); 9426eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette while (viewParent instanceof View) { 9436eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette final View view = (View) viewParent; 9446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if ((ViewCompat.getAlpha(view) <= 0) || (view.getVisibility() != View.VISIBLE)) { 9456eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return false; 9466eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9476eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette viewParent = view.getParent(); 9486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9496eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 9506eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // A null parent implies the view is not visible. 9513b591130c68223aa613660aaff79e65e26b5fc06Zach Kuznia return viewParent != null; 9526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9536eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 9546eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 9556eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Attempts to give accessibility focus to a virtual view. 9566eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 9576eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * A virtual view will not actually take focus if 9586eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * {@link AccessibilityManager#isEnabled()} returns false, 9596eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * {@link AccessibilityManager#isTouchExplorationEnabled()} returns false, 9606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * or the view already has accessibility focus. 9616eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 962049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the identifier of the virtual view on which to 963049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * place accessibility focus 964049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return whether this virtual view actually took accessibility focus 9656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 9666eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette private boolean requestAccessibilityFocus(int virtualViewId) { 9676eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette if (!mManager.isEnabled() 9686eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette || !AccessibilityManagerCompat.isTouchExplorationEnabled(mManager)) { 9696eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return false; 9706eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9716eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // TODO: Check virtual view visibility. 972049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (mAccessibilityFocusedVirtualViewId != virtualViewId) { 973f6a201aaad218a0ee66a9479b16b012a3fd108eaAlan Viverette // Clear focus from the previously focused view, if applicable. 974049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (mAccessibilityFocusedVirtualViewId != INVALID_ID) { 975049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette clearAccessibilityFocus(mAccessibilityFocusedVirtualViewId); 976f6a201aaad218a0ee66a9479b16b012a3fd108eaAlan Viverette } 977f6a201aaad218a0ee66a9479b16b012a3fd108eaAlan Viverette 978f6a201aaad218a0ee66a9479b16b012a3fd108eaAlan Viverette // Set focus on the new view. 979049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette mAccessibilityFocusedVirtualViewId = virtualViewId; 980f6a201aaad218a0ee66a9479b16b012a3fd108eaAlan Viverette 9816eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette // TODO: Only invalidate virtual view bounds. 982049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette mHost.invalidate(); 9836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette sendEventForVirtualView(virtualViewId, 9846eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 9856eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return true; 9866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return false; 9886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 9896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 9906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 9916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Attempts to clear accessibility focus from a virtual view. 992148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette * 993049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the identifier of the virtual view from which to 994049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * clear accessibility focus 995049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return whether this virtual view actually cleared accessibility focus 9966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 997148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette private boolean clearAccessibilityFocus(int virtualViewId) { 998049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (mAccessibilityFocusedVirtualViewId == virtualViewId) { 999049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette mAccessibilityFocusedVirtualViewId = INVALID_ID; 1000049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette mHost.invalidate(); 10016eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette sendEventForVirtualView(virtualViewId, 10026eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); 1003148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette return true; 10046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 1005148d85f65c0f01be809032d2140c7df6d7275504Alan Viverette return false; 10066eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 10076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 10086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 1009049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Attempts to give keyboard focus to a virtual view. 1010049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 1011049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the identifier of the virtual view on which to 1012049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * place keyboard focus 1013049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return whether this virtual view actually took keyboard focus 1014049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 1015049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public final boolean requestKeyboardFocusForVirtualView(int virtualViewId) { 1016049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (!mHost.isFocused() && !mHost.requestFocus()) { 1017049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // Host must have real keyboard focus. 1018049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return false; 1019049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 1020049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 1021049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (mKeyboardFocusedVirtualViewId == virtualViewId) { 1022049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // The virtual view already has focus. 1023049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return false; 1024049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 1025049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 1026049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (mKeyboardFocusedVirtualViewId != INVALID_ID) { 1027049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette clearKeyboardFocusForVirtualView(mKeyboardFocusedVirtualViewId); 1028049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 1029049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 1030049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette mKeyboardFocusedVirtualViewId = virtualViewId; 1031049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 1032049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette onVirtualViewKeyboardFocusChanged(virtualViewId, true); 1033049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_FOCUSED); 1034049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 1035049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return true; 1036049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 1037049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 1038049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 1039049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Attempts to clear keyboard focus from a virtual view. 1040049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * 1041049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @param virtualViewId the identifier of the virtual view from which to 1042049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * clear keyboard focus 1043049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * @return whether this virtual view actually cleared keyboard focus 1044049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette */ 1045049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette public final boolean clearKeyboardFocusForVirtualView(int virtualViewId) { 1046049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette if (mKeyboardFocusedVirtualViewId != virtualViewId) { 1047049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // The virtual view is not focused. 1048049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return false; 1049049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 1050049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 1051049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette mKeyboardFocusedVirtualViewId = INVALID_ID; 1052049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 1053049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette onVirtualViewKeyboardFocusChanged(virtualViewId, false); 1054049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_FOCUSED); 1055049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 1056049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette return true; 1057049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 1058049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette 1059049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette /** 10606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Provides a mapping between view-relative coordinates and logical 10616eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * items. 10626eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 10636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @param x The view-relative x coordinate 10646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @param y The view-relative y coordinate 10656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @return virtual view identifier for the logical item under 106612d8b3f36c7b503221185b44791822e59e690966Alan Viverette * coordinates (x,y) or {@link #HOST_ID} if there is no item at 1067cd9978cdf0c3852189fb881846db6569d43f5598Alan Viverette * the given coordinates 10686eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 10696eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette protected abstract int getVirtualViewAt(float x, float y); 10706eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 10716eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 10726eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Populates a list with the view's visible items. The ordering of items 10736eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * within {@code virtualViewIds} specifies order of accessibility focus 10746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * traversal. 10756eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 10766eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @param virtualViewIds The list to populate with visible items 10776eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 10786eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette protected abstract void getVisibleVirtualViews(List<Integer> virtualViewIds); 10796eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 10806eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 10816eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Populates an {@link AccessibilityEvent} with information about the 10826eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * specified item. 10836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 1084049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * The helper class automatically populates the following fields based on 1085049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * the values set by 1086049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}, 1087049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * but implementations may optionally override them: 10886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <ul> 1089049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>event text, see {@link AccessibilityEvent#getText()} 1090049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>content description, see 1091049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityEvent#setContentDescription(CharSequence)} 1092049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>scrollability, see {@link AccessibilityEvent#setScrollable(boolean)} 1093049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>password state, see {@link AccessibilityEvent#setPassword(boolean)} 1094049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>enabled state, see {@link AccessibilityEvent#setEnabled(boolean)} 1095049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>checked state, see {@link AccessibilityEvent#setChecked(boolean)} 10966eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * </ul> 10976eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 10986eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * The following required fields are automatically populated by the 10996eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * helper class and may not be overridden: 11006eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <ul> 1101049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>item class name, set to the value used in 1102049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)} 11036eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>package name, set to the package of the host view's 11046eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * {@link Context}, see {@link AccessibilityEvent#setPackageName} 11056eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>event source, set to the host view and virtual view identifier, 110614d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas * see {@link AccessibilityRecordCompat#setSource(AccessibilityRecord, View, int)} 11076eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * </ul> 11086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 11096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @param virtualViewId The virtual view id for the item for which to 11106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * populate the event 11116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @param event The event to populate 11126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 1113049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { 1114049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette // Default implementation is no-op. 1115049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette } 11166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 11176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 1118fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette * Populates an {@link AccessibilityEvent} with information about the host 1119fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette * view. 1120fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette * <p> 1121fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette * The default implementation is a no-op. 1122fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette * 1123fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette * @param event the event to populate with information about the host view 1124fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette */ 1125fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette protected void onPopulateEventForHost(AccessibilityEvent event) { 1126fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette // Default implementation is no-op. 1127fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette } 1128fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette 1129fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette /** 11306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Populates an {@link AccessibilityNodeInfoCompat} with information 11316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * about the specified item. 11326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 1133049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Implementations <strong>must</strong> populate the following required 1134049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * fields: 11356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <ul> 1136049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>event text, see 1137049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#setText(CharSequence)} or 1138049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)} 11396eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>bounds in parent coordinates, see 1140049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)} 11416eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * </ul> 11426eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 11436eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * The helper class automatically populates the following fields with 11446eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * default values, but implementations may optionally override them: 11456eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <ul> 1146049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>enabled state, set to {@code true}, see 1147049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#setEnabled(boolean)} 1148049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>keyboard focusability, set to {@code true}, see 1149049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#setFocusable(boolean)} 1150049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>item class name, set to {@code android.view.View}, see 1151049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#setClassName(CharSequence)} 11526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * </ul> 11536eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 11546eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * The following required fields are automatically populated by the 11556eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * helper class and may not be overridden: 11566eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <ul> 11576eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>package name, identical to the package name set by 1158049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link #onPopulateEventForVirtualView(int, AccessibilityEvent)}, see 11596eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * {@link AccessibilityNodeInfoCompat#setPackageName} 11606eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>node source, identical to the event source set in 1161049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link #onPopulateEventForVirtualView(int, AccessibilityEvent)}, see 11626eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * {@link AccessibilityNodeInfoCompat#setSource(View, int)} 11636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>parent view, set to the host view, see 11646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * {@link AccessibilityNodeInfoCompat#setParent(View)} 11656eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>visibility, computed based on parent-relative bounds, see 1166049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#setVisibleToUser(boolean)} 11676eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>accessibility focus, computed based on internal helper state, see 1168049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#setAccessibilityFocused(boolean)} 1169049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * <li>keyboard focus, computed based on internal helper state, see 1170049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#setFocused(boolean)} 11716eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <li>bounds in screen coordinates, computed based on host view bounds, 1172049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * see {@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)} 11736eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * </ul> 11746eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 1175049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Additionally, the helper class automatically handles keyboard focus and 1176049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * accessibility focus management by adding the appropriate 1177049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#ACTION_FOCUS}, 1178049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_FOCUS}, 1179049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS}, or 11806eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_ACCESSIBILITY_FOCUS} 1181049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * actions. Implementations must <strong>never</strong> manually add these 1182049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * actions. 11836eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 11846eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * The helper class also automatically modifies parent- and 11856eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * screen-relative bounds to reflect the portion of the item visible 11866eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * within its parent. 11876eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 11886eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @param virtualViewId The virtual view identifier of the item for 11896eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * which to populate the node 11906eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @param node The node to populate 11916eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 11926eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette protected abstract void onPopulateNodeForVirtualView( 11936eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette int virtualViewId, AccessibilityNodeInfoCompat node); 11946eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 11956eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 119612d8b3f36c7b503221185b44791822e59e690966Alan Viverette * Populates an {@link AccessibilityNodeInfoCompat} with information 119712d8b3f36c7b503221185b44791822e59e690966Alan Viverette * about the host view. 119812d8b3f36c7b503221185b44791822e59e690966Alan Viverette * <p> 1199fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette * The default implementation is a no-op. 1200fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette * 1201fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette * @param node the node to populate with information about the host view 120212d8b3f36c7b503221185b44791822e59e690966Alan Viverette */ 1203fe107ccc9e0ee09fbf7bcb9d17cee970b10ee0cfAlan Viverette protected void onPopulateNodeForHost(AccessibilityNodeInfoCompat node) { 120412d8b3f36c7b503221185b44791822e59e690966Alan Viverette // Default implementation is no-op. 120512d8b3f36c7b503221185b44791822e59e690966Alan Viverette } 120612d8b3f36c7b503221185b44791822e59e690966Alan Viverette 120712d8b3f36c7b503221185b44791822e59e690966Alan Viverette /** 12086eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * Performs the specified accessibility action on the item associated 12096eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * with the virtual view identifier. See 12106eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * {@link AccessibilityNodeInfoCompat#performAction(int, Bundle)} for 12116eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * more information. 12126eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 1213049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Implementations <strong>must</strong> handle any actions added manually 1214049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * in 1215049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}. 12166eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * <p> 12176eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * The helper class automatically handles focus management resulting 12186eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * from {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS} 12196eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * and 12206eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_ACCESSIBILITY_FOCUS} 12216eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * actions. 12226eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * 12236eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @param virtualViewId The virtual view identifier of the item on which 12246eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * to perform the action 12256eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @param action The accessibility action to perform 12266eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @param arguments (Optional) A bundle with additional arguments, or 12276eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * null 12286eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette * @return true if the action was performed 12296eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 12306eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette protected abstract boolean onPerformActionForVirtualView( 12316eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette int virtualViewId, int action, Bundle arguments); 12326eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 12336eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette /** 1234049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette * Exposes a virtual view hierarchy to the accessibility framework. 12356eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette */ 1236049a692ac952be43d5fcd975ec6d394ca058c9d8Alan Viverette private class MyNodeProvider extends AccessibilityNodeProviderCompat { 1237540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas MyNodeProvider() { 1238540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas } 1239540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas 12406eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette @Override 12416eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) { 12427c8264ea497e54aa1536c76e553b52d2207a50fbAlan Viverette // The caller takes ownership of the node and is expected to 12437c8264ea497e54aa1536c76e553b52d2207a50fbAlan Viverette // recycle it when done, so always return a copy. 12447c8264ea497e54aa1536c76e553b52d2207a50fbAlan Viverette final AccessibilityNodeInfoCompat node = 12457c8264ea497e54aa1536c76e553b52d2207a50fbAlan Viverette ExploreByTouchHelper.this.obtainAccessibilityNodeInfo(virtualViewId); 12467c8264ea497e54aa1536c76e553b52d2207a50fbAlan Viverette return AccessibilityNodeInfoCompat.obtain(node); 12476eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 12486eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette 12496eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette @Override 12506eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette public boolean performAction(int virtualViewId, int action, Bundle arguments) { 12516eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette return ExploreByTouchHelper.this.performAction(virtualViewId, action, arguments); 12526eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 12535c0b1da91a9f27f85113990f99f6844030e8c9d8Phil Weaver 12545c0b1da91a9f27f85113990f99f6844030e8c9d8Phil Weaver @Override 12555c0b1da91a9f27f85113990f99f6844030e8c9d8Phil Weaver public AccessibilityNodeInfoCompat findFocus(int focusType) { 12565c0b1da91a9f27f85113990f99f6844030e8c9d8Phil Weaver int focusedId = (focusType == AccessibilityNodeInfoCompat.FOCUS_ACCESSIBILITY) ? 12575c0b1da91a9f27f85113990f99f6844030e8c9d8Phil Weaver mAccessibilityFocusedVirtualViewId : mKeyboardFocusedVirtualViewId; 12585c0b1da91a9f27f85113990f99f6844030e8c9d8Phil Weaver if (focusedId == INVALID_ID) { 12595c0b1da91a9f27f85113990f99f6844030e8c9d8Phil Weaver return null; 12605c0b1da91a9f27f85113990f99f6844030e8c9d8Phil Weaver } 12615c0b1da91a9f27f85113990f99f6844030e8c9d8Phil Weaver return createAccessibilityNodeInfo(focusedId); 12625c0b1da91a9f27f85113990f99f6844030e8c9d8Phil Weaver } 12636eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette } 12646eb3cdf42d5382aef6b6a6afd7c305dbc27885b9Alan Viverette} 1265