Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package android.view.accessibility; |
| 18 | |
| 19 | import android.accessibilityservice.IAccessibilityServiceConnection; |
| 20 | import android.graphics.Rect; |
| 21 | import android.os.Parcel; |
| 22 | import android.os.Parcelable; |
| 23 | import android.os.RemoteException; |
| 24 | import android.text.TextUtils; |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 25 | import android.util.SparseIntArray; |
| 26 | import android.view.View; |
| 27 | |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 28 | import java.util.Collections; |
| 29 | import java.util.List; |
| 30 | |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 31 | /** |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 32 | * This class represents a node of the window content as well as actions that |
| 33 | * can be requested from its source. From the point of view of an |
| 34 | * {@link android.accessibilityservice.AccessibilityService} a window content is |
| 35 | * presented as tree of accessibility node info which may or may not map one-to-one |
| 36 | * to the view hierarchy. In other words, a custom view is free to report itself as |
| 37 | * a tree of accessibility node info. |
| 38 | * </p> |
| 39 | * <p> |
| 40 | * Once an accessibility node info is delivered to an accessibility service it is |
| 41 | * made immutable and calling a state mutation method generates an error. |
| 42 | * </p> |
| 43 | * <p> |
| 44 | * Please refer to {@link android.accessibilityservice.AccessibilityService} for |
| 45 | * details about how to obtain a handle to window content as a tree of accessibility |
| 46 | * node info as well as familiarizing with the security model. |
| 47 | * </p> |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 48 | * |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 49 | * @see android.accessibilityservice.AccessibilityService |
| 50 | * @see AccessibilityEvent |
| 51 | * @see AccessibilityManager |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 52 | */ |
| 53 | public class AccessibilityNodeInfo implements Parcelable { |
| 54 | |
| 55 | private static final boolean DEBUG = false; |
| 56 | |
| 57 | // Actions. |
| 58 | |
| 59 | /** |
| 60 | * Action that focuses the node. |
| 61 | */ |
| 62 | public static final int ACTION_FOCUS = 0x00000001; |
| 63 | |
| 64 | /** |
| 65 | * Action that unfocuses the node. |
| 66 | */ |
| 67 | public static final int ACTION_CLEAR_FOCUS = 0x00000002; |
| 68 | |
| 69 | /** |
| 70 | * Action that selects the node. |
| 71 | */ |
| 72 | public static final int ACTION_SELECT = 0x00000004; |
| 73 | |
| 74 | /** |
| 75 | * Action that unselects the node. |
| 76 | */ |
| 77 | public static final int ACTION_CLEAR_SELECTION = 0x00000008; |
| 78 | |
| 79 | // Boolean attributes. |
| 80 | |
| 81 | private static final int PROPERTY_CHECKABLE = 0x00000001; |
| 82 | |
| 83 | private static final int PROPERTY_CHECKED = 0x00000002; |
| 84 | |
| 85 | private static final int PROPERTY_FOCUSABLE = 0x00000004; |
| 86 | |
| 87 | private static final int PROPERTY_FOCUSED = 0x00000008; |
| 88 | |
| 89 | private static final int PROPERTY_SELECTED = 0x00000010; |
| 90 | |
| 91 | private static final int PROPERTY_CLICKABLE = 0x00000020; |
| 92 | |
| 93 | private static final int PROPERTY_LONG_CLICKABLE = 0x00000040; |
| 94 | |
| 95 | private static final int PROPERTY_ENABLED = 0x00000080; |
| 96 | |
| 97 | private static final int PROPERTY_PASSWORD = 0x00000100; |
| 98 | |
Svetoslav Ganov | a015617 | 2011-06-26 17:55:44 -0700 | [diff] [blame] | 99 | private static final int PROPERTY_SCROLLABLE = 0x00000200; |
| 100 | |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 101 | // Housekeeping. |
| 102 | private static final int MAX_POOL_SIZE = 50; |
| 103 | private static final Object sPoolLock = new Object(); |
| 104 | private static AccessibilityNodeInfo sPool; |
| 105 | private static int sPoolSize; |
| 106 | private AccessibilityNodeInfo mNext; |
| 107 | private boolean mIsInPool; |
| 108 | private boolean mSealed; |
| 109 | |
| 110 | // Data. |
Svetoslav Ganov | 9210ccb | 2011-06-08 19:42:51 -0700 | [diff] [blame] | 111 | private int mAccessibilityViewId = View.NO_ID; |
| 112 | private int mAccessibilityWindowId = View.NO_ID; |
| 113 | private int mParentAccessibilityViewId = View.NO_ID; |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 114 | private int mBooleanProperties; |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 115 | private final Rect mBoundsInParent = new Rect(); |
| 116 | private final Rect mBoundsInScreen = new Rect(); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 117 | |
| 118 | private CharSequence mPackageName; |
| 119 | private CharSequence mClassName; |
| 120 | private CharSequence mText; |
| 121 | private CharSequence mContentDescription; |
| 122 | |
Svetoslav Ganov | 35bfede | 2011-07-14 17:57:06 -0700 | [diff] [blame] | 123 | private SparseIntArray mChildAccessibilityIds = new SparseIntArray(); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 124 | private int mActions; |
| 125 | |
| 126 | private IAccessibilityServiceConnection mConnection; |
| 127 | |
| 128 | /** |
| 129 | * Hide constructor from clients. |
| 130 | */ |
| 131 | private AccessibilityNodeInfo() { |
| 132 | /* do nothing */ |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * Sets the source. |
| 137 | * |
| 138 | * @param source The info source. |
| 139 | */ |
| 140 | public void setSource(View source) { |
| 141 | enforceNotSealed(); |
| 142 | mAccessibilityViewId = source.getAccessibilityViewId(); |
| 143 | mAccessibilityWindowId = source.getAccessibilityWindowId(); |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * Gets the id of the window from which the info comes from. |
| 148 | * |
| 149 | * @return The window id. |
| 150 | */ |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 151 | public int getWindowId() { |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 152 | return mAccessibilityWindowId; |
| 153 | } |
| 154 | |
| 155 | /** |
| 156 | * Gets the number of children. |
| 157 | * |
| 158 | * @return The child count. |
| 159 | */ |
| 160 | public int getChildCount() { |
| 161 | return mChildAccessibilityIds.size(); |
| 162 | } |
| 163 | |
| 164 | /** |
| 165 | * Get the child at given index. |
| 166 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 167 | * <strong>Note:</strong> It is a client responsibility to recycle the |
| 168 | * received info by calling {@link AccessibilityNodeInfo#recycle()} |
| 169 | * to avoid creating of multiple instances. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 170 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 171 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 172 | * @param index The child index. |
| 173 | * @return The child node. |
| 174 | * |
| 175 | * @throws IllegalStateException If called outside of an AccessibilityService. |
| 176 | * |
| 177 | */ |
| 178 | public AccessibilityNodeInfo getChild(int index) { |
| 179 | enforceSealed(); |
| 180 | final int childAccessibilityViewId = mChildAccessibilityIds.get(index); |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 181 | if (!canPerformRequestOverConnection(childAccessibilityViewId)) { |
| 182 | return null; |
| 183 | } |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 184 | try { |
| 185 | return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId, |
| 186 | childAccessibilityViewId); |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 187 | } catch (RemoteException re) { |
| 188 | /* ignore*/ |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 189 | } |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 190 | return null; |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 191 | } |
| 192 | |
| 193 | /** |
| 194 | * Adds a child. |
| 195 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 196 | * <strong>Note:</strong> Cannot be called from an |
| 197 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 198 | * This class is made immutable before being delivered to an AccessibilityService. |
| 199 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 200 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 201 | * @param child The child. |
| 202 | * |
| 203 | * @throws IllegalStateException If called from an AccessibilityService. |
| 204 | */ |
| 205 | public void addChild(View child) { |
| 206 | enforceNotSealed(); |
| 207 | final int childAccessibilityViewId = child.getAccessibilityViewId(); |
| 208 | final int index = mChildAccessibilityIds.size(); |
| 209 | mChildAccessibilityIds.put(index, childAccessibilityViewId); |
| 210 | } |
| 211 | |
| 212 | /** |
| 213 | * Gets the actions that can be performed on the node. |
| 214 | * |
| 215 | * @return The bit mask of with actions. |
| 216 | * |
| 217 | * @see AccessibilityNodeInfo#ACTION_FOCUS |
| 218 | * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS |
| 219 | * @see AccessibilityNodeInfo#ACTION_SELECT |
| 220 | * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION |
| 221 | */ |
| 222 | public int getActions() { |
| 223 | return mActions; |
| 224 | } |
| 225 | |
| 226 | /** |
| 227 | * Adds an action that can be performed on the node. |
| 228 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 229 | * <strong>Note:</strong> Cannot be called from an |
| 230 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 231 | * This class is made immutable before being delivered to an AccessibilityService. |
| 232 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 233 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 234 | * @param action The action. |
| 235 | * |
| 236 | * @throws IllegalStateException If called from an AccessibilityService. |
| 237 | */ |
| 238 | public void addAction(int action) { |
| 239 | enforceNotSealed(); |
| 240 | mActions |= action; |
| 241 | } |
| 242 | |
| 243 | /** |
| 244 | * Performs an action on the node. |
| 245 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 246 | * <strong>Note:</strong> An action can be performed only if the request is made |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 247 | * from an {@link android.accessibilityservice.AccessibilityService}. |
| 248 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 249 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 250 | * @param action The action to perform. |
| 251 | * @return True if the action was performed. |
| 252 | * |
| 253 | * @throws IllegalStateException If called outside of an AccessibilityService. |
| 254 | */ |
| 255 | public boolean performAction(int action) { |
| 256 | enforceSealed(); |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 257 | if (!canPerformRequestOverConnection(mAccessibilityViewId)) { |
| 258 | return false; |
| 259 | } |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 260 | try { |
| 261 | return mConnection.performAccessibilityAction(mAccessibilityWindowId, |
| 262 | mAccessibilityViewId, action); |
| 263 | } catch (RemoteException e) { |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 264 | /* ignore */ |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 265 | } |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 266 | return false; |
| 267 | } |
| 268 | |
| 269 | /** |
| 270 | * Finds {@link AccessibilityNodeInfo}s by text. The match is case |
Svetoslav Ganov | 86398bd | 2011-06-21 17:38:43 -0700 | [diff] [blame] | 271 | * insensitive containment. The search is relative to this info i.e. |
| 272 | * this info is the root of the traversed tree. |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 273 | * <p> |
| 274 | * <strong>Note:</strong> It is a client responsibility to recycle the |
| 275 | * received info by calling {@link AccessibilityNodeInfo#recycle()} |
| 276 | * to avoid creating of multiple instances. |
| 277 | * </p> |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 278 | * |
| 279 | * @param text The searched text. |
| 280 | * @return A list of node info. |
| 281 | */ |
| 282 | public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) { |
| 283 | enforceSealed(); |
| 284 | if (!canPerformRequestOverConnection(mAccessibilityViewId)) { |
Svetoslav Ganov | 86398bd | 2011-06-21 17:38:43 -0700 | [diff] [blame] | 285 | return Collections.emptyList(); |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 286 | } |
| 287 | try { |
| 288 | return mConnection.findAccessibilityNodeInfosByViewText(text, mAccessibilityWindowId, |
| 289 | mAccessibilityViewId); |
| 290 | } catch (RemoteException e) { |
| 291 | /* ignore */ |
| 292 | } |
| 293 | return Collections.emptyList(); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 294 | } |
| 295 | |
| 296 | /** |
Svetoslav Ganov | 00aabf7 | 2011-07-21 11:35:03 -0700 | [diff] [blame^] | 297 | * Gets the parent. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 298 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 299 | * <strong>Note:</strong> It is a client responsibility to recycle the |
| 300 | * received info by calling {@link AccessibilityNodeInfo#recycle()} |
| 301 | * to avoid creating of multiple instances. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 302 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 303 | * |
Svetoslav Ganov | 00aabf7 | 2011-07-21 11:35:03 -0700 | [diff] [blame^] | 304 | * @return The parent. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 305 | */ |
| 306 | public AccessibilityNodeInfo getParent() { |
| 307 | enforceSealed(); |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 308 | if (!canPerformRequestOverConnection(mAccessibilityViewId)) { |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 309 | return null; |
| 310 | } |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 311 | try { |
| 312 | return mConnection.findAccessibilityNodeInfoByAccessibilityId( |
| 313 | mAccessibilityWindowId, mParentAccessibilityViewId); |
| 314 | } catch (RemoteException e) { |
| 315 | /* ignore */ |
| 316 | } |
| 317 | return null; |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 318 | } |
| 319 | |
| 320 | /** |
| 321 | * Sets the parent. |
| 322 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 323 | * <strong>Note:</strong> Cannot be called from an |
| 324 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 325 | * This class is made immutable before being delivered to an AccessibilityService. |
| 326 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 327 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 328 | * @param parent The parent. |
| 329 | * |
| 330 | * @throws IllegalStateException If called from an AccessibilityService. |
| 331 | */ |
| 332 | public void setParent(View parent) { |
| 333 | enforceNotSealed(); |
| 334 | mParentAccessibilityViewId = parent.getAccessibilityViewId(); |
| 335 | } |
| 336 | |
| 337 | /** |
| 338 | * Gets the node bounds in parent coordinates. |
| 339 | * |
| 340 | * @param outBounds The output node bounds. |
| 341 | */ |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 342 | public void getBoundsInParent(Rect outBounds) { |
| 343 | outBounds.set(mBoundsInParent.left, mBoundsInParent.top, |
| 344 | mBoundsInParent.right, mBoundsInParent.bottom); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 345 | } |
| 346 | |
| 347 | /** |
| 348 | * Sets the node bounds in parent coordinates. |
| 349 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 350 | * <strong>Note:</strong> Cannot be called from an |
| 351 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 352 | * This class is made immutable before being delivered to an AccessibilityService. |
| 353 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 354 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 355 | * @param bounds The node bounds. |
| 356 | * |
| 357 | * @throws IllegalStateException If called from an AccessibilityService. |
| 358 | */ |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 359 | public void setBoundsInParent(Rect bounds) { |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 360 | enforceNotSealed(); |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 361 | mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom); |
| 362 | } |
| 363 | |
| 364 | /** |
| 365 | * Gets the node bounds in screen coordinates. |
| 366 | * |
| 367 | * @param outBounds The output node bounds. |
| 368 | */ |
| 369 | public void getBoundsInScreen(Rect outBounds) { |
| 370 | outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top, |
| 371 | mBoundsInScreen.right, mBoundsInScreen.bottom); |
| 372 | } |
| 373 | |
| 374 | /** |
| 375 | * Sets the node bounds in screen coordinates. |
| 376 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 377 | * <strong>Note:</strong> Cannot be called from an |
| 378 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 379 | * This class is made immutable before being delivered to an AccessibilityService. |
| 380 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 381 | * |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 382 | * @param bounds The node bounds. |
| 383 | * |
| 384 | * @throws IllegalStateException If called from an AccessibilityService. |
| 385 | */ |
| 386 | public void setBoundsInScreen(Rect bounds) { |
| 387 | enforceNotSealed(); |
| 388 | mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 389 | } |
| 390 | |
| 391 | /** |
| 392 | * Gets whether this node is checkable. |
| 393 | * |
| 394 | * @return True if the node is checkable. |
| 395 | */ |
| 396 | public boolean isCheckable() { |
| 397 | return getBooleanProperty(PROPERTY_CHECKABLE); |
| 398 | } |
| 399 | |
| 400 | /** |
| 401 | * Sets whether this node is checkable. |
| 402 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 403 | * <strong>Note:</strong> Cannot be called from an |
| 404 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 405 | * This class is made immutable before being delivered to an AccessibilityService. |
| 406 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 407 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 408 | * @param checkable True if the node is checkable. |
| 409 | * |
| 410 | * @throws IllegalStateException If called from an AccessibilityService. |
| 411 | */ |
| 412 | public void setCheckable(boolean checkable) { |
| 413 | setBooleanProperty(PROPERTY_CHECKABLE, checkable); |
| 414 | } |
| 415 | |
| 416 | /** |
| 417 | * Gets whether this node is checked. |
| 418 | * |
| 419 | * @return True if the node is checked. |
| 420 | */ |
| 421 | public boolean isChecked() { |
| 422 | return getBooleanProperty(PROPERTY_CHECKED); |
| 423 | } |
| 424 | |
| 425 | /** |
| 426 | * Sets whether this node is checked. |
| 427 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 428 | * <strong>Note:</strong> Cannot be called from an |
| 429 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 430 | * This class is made immutable before being delivered to an AccessibilityService. |
| 431 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 432 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 433 | * @param checked True if the node is checked. |
| 434 | * |
| 435 | * @throws IllegalStateException If called from an AccessibilityService. |
| 436 | */ |
| 437 | public void setChecked(boolean checked) { |
| 438 | setBooleanProperty(PROPERTY_CHECKED, checked); |
| 439 | } |
| 440 | |
| 441 | /** |
| 442 | * Gets whether this node is focusable. |
| 443 | * |
| 444 | * @return True if the node is focusable. |
| 445 | */ |
| 446 | public boolean isFocusable() { |
| 447 | return getBooleanProperty(PROPERTY_FOCUSABLE); |
| 448 | } |
| 449 | |
| 450 | /** |
| 451 | * Sets whether this node is focusable. |
| 452 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 453 | * <strong>Note:</strong> Cannot be called from an |
| 454 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 455 | * This class is made immutable before being delivered to an AccessibilityService. |
| 456 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 457 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 458 | * @param focusable True if the node is focusable. |
| 459 | * |
| 460 | * @throws IllegalStateException If called from an AccessibilityService. |
| 461 | */ |
| 462 | public void setFocusable(boolean focusable) { |
| 463 | setBooleanProperty(PROPERTY_FOCUSABLE, focusable); |
| 464 | } |
| 465 | |
| 466 | /** |
| 467 | * Gets whether this node is focused. |
| 468 | * |
| 469 | * @return True if the node is focused. |
| 470 | */ |
| 471 | public boolean isFocused() { |
| 472 | return getBooleanProperty(PROPERTY_FOCUSED); |
| 473 | } |
| 474 | |
| 475 | /** |
| 476 | * Sets whether this node is focused. |
| 477 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 478 | * <strong>Note:</strong> Cannot be called from an |
| 479 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 480 | * This class is made immutable before being delivered to an AccessibilityService. |
| 481 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 482 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 483 | * @param focused True if the node is focused. |
| 484 | * |
| 485 | * @throws IllegalStateException If called from an AccessibilityService. |
| 486 | */ |
| 487 | public void setFocused(boolean focused) { |
| 488 | setBooleanProperty(PROPERTY_FOCUSED, focused); |
| 489 | } |
| 490 | |
| 491 | /** |
| 492 | * Gets whether this node is selected. |
| 493 | * |
| 494 | * @return True if the node is selected. |
| 495 | */ |
| 496 | public boolean isSelected() { |
| 497 | return getBooleanProperty(PROPERTY_SELECTED); |
| 498 | } |
| 499 | |
| 500 | /** |
| 501 | * Sets whether this node is selected. |
| 502 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 503 | * <strong>Note:</strong> Cannot be called from an |
| 504 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 505 | * This class is made immutable before being delivered to an AccessibilityService. |
| 506 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 507 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 508 | * @param selected True if the node is selected. |
| 509 | * |
| 510 | * @throws IllegalStateException If called from an AccessibilityService. |
| 511 | */ |
| 512 | public void setSelected(boolean selected) { |
| 513 | setBooleanProperty(PROPERTY_SELECTED, selected); |
| 514 | } |
| 515 | |
| 516 | /** |
| 517 | * Gets whether this node is clickable. |
| 518 | * |
| 519 | * @return True if the node is clickable. |
| 520 | */ |
| 521 | public boolean isClickable() { |
| 522 | return getBooleanProperty(PROPERTY_CLICKABLE); |
| 523 | } |
| 524 | |
| 525 | /** |
| 526 | * Sets whether this node is clickable. |
| 527 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 528 | * <strong>Note:</strong> Cannot be called from an |
| 529 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 530 | * This class is made immutable before being delivered to an AccessibilityService. |
| 531 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 532 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 533 | * @param clickable True if the node is clickable. |
| 534 | * |
| 535 | * @throws IllegalStateException If called from an AccessibilityService. |
| 536 | */ |
| 537 | public void setClickable(boolean clickable) { |
| 538 | setBooleanProperty(PROPERTY_CLICKABLE, clickable); |
| 539 | } |
| 540 | |
| 541 | /** |
| 542 | * Gets whether this node is long clickable. |
| 543 | * |
| 544 | * @return True if the node is long clickable. |
| 545 | */ |
| 546 | public boolean isLongClickable() { |
| 547 | return getBooleanProperty(PROPERTY_LONG_CLICKABLE); |
| 548 | } |
| 549 | |
| 550 | /** |
| 551 | * Sets whether this node is long clickable. |
| 552 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 553 | * <strong>Note:</strong> Cannot be called from an |
| 554 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 555 | * This class is made immutable before being delivered to an AccessibilityService. |
| 556 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 557 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 558 | * @param longClickable True if the node is long clickable. |
| 559 | * |
| 560 | * @throws IllegalStateException If called from an AccessibilityService. |
| 561 | */ |
| 562 | public void setLongClickable(boolean longClickable) { |
| 563 | setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable); |
| 564 | } |
| 565 | |
| 566 | /** |
| 567 | * Gets whether this node is enabled. |
| 568 | * |
| 569 | * @return True if the node is enabled. |
| 570 | */ |
| 571 | public boolean isEnabled() { |
| 572 | return getBooleanProperty(PROPERTY_ENABLED); |
| 573 | } |
| 574 | |
| 575 | /** |
| 576 | * Sets whether this node is enabled. |
| 577 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 578 | * <strong>Note:</strong> Cannot be called from an |
| 579 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 580 | * This class is made immutable before being delivered to an AccessibilityService. |
| 581 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 582 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 583 | * @param enabled True if the node is enabled. |
| 584 | * |
| 585 | * @throws IllegalStateException If called from an AccessibilityService. |
| 586 | */ |
| 587 | public void setEnabled(boolean enabled) { |
| 588 | setBooleanProperty(PROPERTY_ENABLED, enabled); |
| 589 | } |
| 590 | |
| 591 | /** |
| 592 | * Gets whether this node is a password. |
| 593 | * |
| 594 | * @return True if the node is a password. |
| 595 | */ |
| 596 | public boolean isPassword() { |
| 597 | return getBooleanProperty(PROPERTY_PASSWORD); |
| 598 | } |
| 599 | |
| 600 | /** |
| 601 | * Sets whether this node is a password. |
| 602 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 603 | * <strong>Note:</strong> Cannot be called from an |
| 604 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 605 | * This class is made immutable before being delivered to an AccessibilityService. |
| 606 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 607 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 608 | * @param password True if the node is a password. |
| 609 | * |
| 610 | * @throws IllegalStateException If called from an AccessibilityService. |
| 611 | */ |
| 612 | public void setPassword(boolean password) { |
| 613 | setBooleanProperty(PROPERTY_PASSWORD, password); |
| 614 | } |
| 615 | |
| 616 | /** |
Svetoslav Ganov | a015617 | 2011-06-26 17:55:44 -0700 | [diff] [blame] | 617 | * Gets if the node is scrollable. |
| 618 | * |
| 619 | * @return True if the node is scrollable, false otherwise. |
| 620 | */ |
| 621 | public boolean isScrollable() { |
| 622 | return getBooleanProperty(PROPERTY_SCROLLABLE); |
| 623 | } |
| 624 | |
| 625 | /** |
| 626 | * Sets if the node is scrollable. |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 627 | * <p> |
| 628 | * <strong>Note:</strong> Cannot be called from an |
| 629 | * {@link android.accessibilityservice.AccessibilityService}. |
| 630 | * This class is made immutable before being delivered to an AccessibilityService. |
| 631 | * </p> |
Svetoslav Ganov | a015617 | 2011-06-26 17:55:44 -0700 | [diff] [blame] | 632 | * |
| 633 | * @param scrollable True if the node is scrollable, false otherwise. |
| 634 | * |
| 635 | * @throws IllegalStateException If called from an AccessibilityService. |
| 636 | */ |
| 637 | public void setScrollable(boolean scrollable) { |
| 638 | enforceNotSealed(); |
| 639 | setBooleanProperty(PROPERTY_SCROLLABLE, scrollable); |
| 640 | } |
| 641 | |
| 642 | /** |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 643 | * Gets the package this node comes from. |
| 644 | * |
| 645 | * @return The package name. |
| 646 | */ |
| 647 | public CharSequence getPackageName() { |
| 648 | return mPackageName; |
| 649 | } |
| 650 | |
| 651 | /** |
| 652 | * Sets the package this node comes from. |
| 653 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 654 | * <strong>Note:</strong> Cannot be called from an |
| 655 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 656 | * This class is made immutable before being delivered to an AccessibilityService. |
| 657 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 658 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 659 | * @param packageName The package name. |
| 660 | * |
| 661 | * @throws IllegalStateException If called from an AccessibilityService. |
| 662 | */ |
| 663 | public void setPackageName(CharSequence packageName) { |
| 664 | enforceNotSealed(); |
| 665 | mPackageName = packageName; |
| 666 | } |
| 667 | |
| 668 | /** |
| 669 | * Gets the class this node comes from. |
| 670 | * |
| 671 | * @return The class name. |
| 672 | */ |
| 673 | public CharSequence getClassName() { |
| 674 | return mClassName; |
| 675 | } |
| 676 | |
| 677 | /** |
| 678 | * Sets the class this node comes from. |
| 679 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 680 | * <strong>Note:</strong> Cannot be called from an |
| 681 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 682 | * This class is made immutable before being delivered to an AccessibilityService. |
| 683 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 684 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 685 | * @param className The class name. |
| 686 | * |
| 687 | * @throws IllegalStateException If called from an AccessibilityService. |
| 688 | */ |
| 689 | public void setClassName(CharSequence className) { |
| 690 | enforceNotSealed(); |
| 691 | mClassName = className; |
| 692 | } |
| 693 | |
| 694 | /** |
| 695 | * Gets the text of this node. |
| 696 | * |
| 697 | * @return The text. |
| 698 | */ |
| 699 | public CharSequence getText() { |
| 700 | return mText; |
| 701 | } |
| 702 | |
| 703 | /** |
| 704 | * Sets the text of this node. |
| 705 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 706 | * <strong>Note:</strong> Cannot be called from an |
| 707 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 708 | * This class is made immutable before being delivered to an AccessibilityService. |
| 709 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 710 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 711 | * @param text The text. |
| 712 | * |
| 713 | * @throws IllegalStateException If called from an AccessibilityService. |
| 714 | */ |
| 715 | public void setText(CharSequence text) { |
| 716 | enforceNotSealed(); |
| 717 | mText = text; |
| 718 | } |
| 719 | |
| 720 | /** |
| 721 | * Gets the content description of this node. |
| 722 | * |
| 723 | * @return The content description. |
| 724 | */ |
| 725 | public CharSequence getContentDescription() { |
| 726 | return mContentDescription; |
| 727 | } |
| 728 | |
| 729 | /** |
| 730 | * Sets the content description of this node. |
| 731 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 732 | * <strong>Note:</strong> Cannot be called from an |
| 733 | * {@link android.accessibilityservice.AccessibilityService}. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 734 | * This class is made immutable before being delivered to an AccessibilityService. |
| 735 | * </p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 736 | * |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 737 | * @param contentDescription The content description. |
| 738 | * |
| 739 | * @throws IllegalStateException If called from an AccessibilityService. |
| 740 | */ |
| 741 | public void setContentDescription(CharSequence contentDescription) { |
| 742 | enforceNotSealed(); |
| 743 | mContentDescription = contentDescription; |
| 744 | } |
| 745 | |
| 746 | /** |
| 747 | * Gets the value of a boolean property. |
| 748 | * |
| 749 | * @param property The property. |
| 750 | * @return The value. |
| 751 | */ |
| 752 | private boolean getBooleanProperty(int property) { |
| 753 | return (mBooleanProperties & property) != 0; |
| 754 | } |
| 755 | |
| 756 | /** |
| 757 | * Sets a boolean property. |
| 758 | * |
| 759 | * @param property The property. |
| 760 | * @param value The value. |
| 761 | * |
| 762 | * @throws IllegalStateException If called from an AccessibilityService. |
| 763 | */ |
| 764 | private void setBooleanProperty(int property, boolean value) { |
| 765 | enforceNotSealed(); |
| 766 | if (value) { |
| 767 | mBooleanProperties |= property; |
| 768 | } else { |
| 769 | mBooleanProperties &= ~property; |
| 770 | } |
| 771 | } |
| 772 | |
| 773 | /** |
| 774 | * Sets the connection for interacting with the system. |
| 775 | * |
| 776 | * @param connection The client token. |
| 777 | * |
| 778 | * @hide |
| 779 | */ |
| 780 | public final void setConnection(IAccessibilityServiceConnection connection) { |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 781 | enforceNotSealed(); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 782 | mConnection = connection; |
| 783 | } |
| 784 | |
| 785 | /** |
| 786 | * {@inheritDoc} |
| 787 | */ |
| 788 | public int describeContents() { |
| 789 | return 0; |
| 790 | } |
| 791 | |
| 792 | /** |
| 793 | * Sets if this instance is sealed. |
| 794 | * |
| 795 | * @param sealed Whether is sealed. |
| 796 | * |
| 797 | * @hide |
| 798 | */ |
| 799 | public void setSealed(boolean sealed) { |
| 800 | mSealed = sealed; |
| 801 | } |
| 802 | |
| 803 | /** |
| 804 | * Gets if this instance is sealed. |
| 805 | * |
| 806 | * @return Whether is sealed. |
| 807 | * |
| 808 | * @hide |
| 809 | */ |
| 810 | public boolean isSealed() { |
| 811 | return mSealed; |
| 812 | } |
| 813 | |
| 814 | /** |
| 815 | * Enforces that this instance is sealed. |
| 816 | * |
| 817 | * @throws IllegalStateException If this instance is not sealed. |
| 818 | * |
| 819 | * @hide |
| 820 | */ |
| 821 | protected void enforceSealed() { |
| 822 | if (!isSealed()) { |
| 823 | throw new IllegalStateException("Cannot perform this " |
| 824 | + "action on a not sealed instance."); |
| 825 | } |
| 826 | } |
| 827 | |
| 828 | /** |
| 829 | * Enforces that this instance is not sealed. |
| 830 | * |
| 831 | * @throws IllegalStateException If this instance is sealed. |
| 832 | * |
| 833 | * @hide |
| 834 | */ |
| 835 | protected void enforceNotSealed() { |
| 836 | if (isSealed()) { |
| 837 | throw new IllegalStateException("Cannot perform this " |
| 838 | + "action on an sealed instance."); |
| 839 | } |
| 840 | } |
| 841 | |
| 842 | /** |
| 843 | * Returns a cached instance if such is available otherwise a new one |
| 844 | * and sets the source. |
| 845 | * |
| 846 | * @return An instance. |
| 847 | * |
| 848 | * @see #setSource(View) |
| 849 | */ |
| 850 | public static AccessibilityNodeInfo obtain(View source) { |
| 851 | AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); |
| 852 | info.setSource(source); |
| 853 | return info; |
| 854 | } |
| 855 | |
| 856 | /** |
| 857 | * Returns a cached instance if such is available otherwise a new one. |
| 858 | * |
| 859 | * @return An instance. |
| 860 | */ |
| 861 | public static AccessibilityNodeInfo obtain() { |
| 862 | synchronized (sPoolLock) { |
| 863 | if (sPool != null) { |
| 864 | AccessibilityNodeInfo info = sPool; |
| 865 | sPool = sPool.mNext; |
| 866 | sPoolSize--; |
| 867 | info.mNext = null; |
| 868 | info.mIsInPool = false; |
Svetoslav Ganov | 9210ccb | 2011-06-08 19:42:51 -0700 | [diff] [blame] | 869 | return info; |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 870 | } |
| 871 | return new AccessibilityNodeInfo(); |
| 872 | } |
| 873 | } |
| 874 | |
| 875 | /** |
Svetoslav Ganov | 35bfede | 2011-07-14 17:57:06 -0700 | [diff] [blame] | 876 | * Returns a cached instance if such is available or a new one is |
| 877 | * create. The returned instance is initialized from the given |
| 878 | * <code>info</code>. |
| 879 | * |
| 880 | * @param info The other info. |
| 881 | * @return An instance. |
| 882 | */ |
| 883 | public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) { |
| 884 | AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain(); |
| 885 | infoClone.init(info); |
| 886 | return infoClone; |
| 887 | } |
| 888 | |
| 889 | /** |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 890 | * Return an instance back to be reused. |
| 891 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 892 | * <strong>Note:</strong> You must not touch the object after calling this function. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 893 | * |
| 894 | * @throws IllegalStateException If the info is already recycled. |
| 895 | */ |
| 896 | public void recycle() { |
| 897 | if (mIsInPool) { |
| 898 | throw new IllegalStateException("Info already recycled!"); |
| 899 | } |
| 900 | clear(); |
| 901 | synchronized (sPoolLock) { |
| 902 | if (sPoolSize <= MAX_POOL_SIZE) { |
| 903 | mNext = sPool; |
| 904 | sPool = this; |
| 905 | mIsInPool = true; |
| 906 | sPoolSize++; |
| 907 | } |
| 908 | } |
| 909 | } |
| 910 | |
| 911 | /** |
| 912 | * {@inheritDoc} |
| 913 | * <p> |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 914 | * <strong>Note:</strong> After the instance is written to a parcel it |
| 915 | * is recycled. You must not touch the object after calling this function. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 916 | * </p> |
| 917 | */ |
| 918 | public void writeToParcel(Parcel parcel, int flags) { |
| 919 | if (mConnection == null) { |
| 920 | parcel.writeInt(0); |
| 921 | } else { |
| 922 | parcel.writeInt(1); |
| 923 | parcel.writeStrongBinder(mConnection.asBinder()); |
| 924 | } |
| 925 | parcel.writeInt(isSealed() ? 1 : 0); |
| 926 | parcel.writeInt(mAccessibilityViewId); |
| 927 | parcel.writeInt(mAccessibilityWindowId); |
| 928 | parcel.writeInt(mParentAccessibilityViewId); |
| 929 | |
| 930 | SparseIntArray childIds = mChildAccessibilityIds; |
| 931 | final int childIdsSize = childIds.size(); |
| 932 | parcel.writeInt(childIdsSize); |
| 933 | for (int i = 0; i < childIdsSize; i++) { |
| 934 | parcel.writeInt(childIds.valueAt(i)); |
| 935 | } |
| 936 | |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 937 | parcel.writeInt(mBoundsInParent.top); |
| 938 | parcel.writeInt(mBoundsInParent.bottom); |
| 939 | parcel.writeInt(mBoundsInParent.left); |
| 940 | parcel.writeInt(mBoundsInParent.right); |
| 941 | |
| 942 | parcel.writeInt(mBoundsInScreen.top); |
| 943 | parcel.writeInt(mBoundsInScreen.bottom); |
| 944 | parcel.writeInt(mBoundsInScreen.left); |
| 945 | parcel.writeInt(mBoundsInScreen.right); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 946 | |
| 947 | parcel.writeInt(mActions); |
| 948 | |
| 949 | parcel.writeInt(mBooleanProperties); |
| 950 | |
| 951 | TextUtils.writeToParcel(mPackageName, parcel, flags); |
| 952 | TextUtils.writeToParcel(mClassName, parcel, flags); |
| 953 | TextUtils.writeToParcel(mText, parcel, flags); |
| 954 | TextUtils.writeToParcel(mContentDescription, parcel, flags); |
| 955 | |
| 956 | // Since instances of this class are fetched via synchronous i.e. blocking |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 957 | // calls in IPCs we always recycle as soon as the instance is marshaled. |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 958 | recycle(); |
| 959 | } |
| 960 | |
| 961 | /** |
Svetoslav Ganov | 35bfede | 2011-07-14 17:57:06 -0700 | [diff] [blame] | 962 | * Initializes this instance from another one. |
| 963 | * |
| 964 | * @param other The other instance. |
| 965 | */ |
| 966 | private void init(AccessibilityNodeInfo other) { |
| 967 | mSealed = other.mSealed; |
| 968 | mConnection = other.mConnection; |
| 969 | mAccessibilityViewId = other.mAccessibilityViewId; |
| 970 | mParentAccessibilityViewId = other.mParentAccessibilityViewId; |
| 971 | mAccessibilityWindowId = other.mAccessibilityWindowId; |
| 972 | mBoundsInParent.set(other.mBoundsInParent); |
| 973 | mBoundsInScreen.set(other.mBoundsInScreen); |
| 974 | mPackageName = other.mPackageName; |
| 975 | mClassName = other.mClassName; |
| 976 | mText = other.mText; |
| 977 | mContentDescription = other.mContentDescription; |
| 978 | mActions= other.mActions; |
| 979 | mBooleanProperties = other.mBooleanProperties; |
| 980 | mChildAccessibilityIds = other.mChildAccessibilityIds.clone(); |
| 981 | } |
| 982 | |
| 983 | /** |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 984 | * Creates a new instance from a {@link Parcel}. |
| 985 | * |
| 986 | * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. |
| 987 | */ |
| 988 | private void initFromParcel(Parcel parcel) { |
| 989 | if (parcel.readInt() == 1) { |
| 990 | mConnection = IAccessibilityServiceConnection.Stub.asInterface( |
| 991 | parcel.readStrongBinder()); |
| 992 | } |
| 993 | mSealed = (parcel.readInt() == 1); |
| 994 | mAccessibilityViewId = parcel.readInt(); |
| 995 | mAccessibilityWindowId = parcel.readInt(); |
| 996 | mParentAccessibilityViewId = parcel.readInt(); |
| 997 | |
| 998 | SparseIntArray childIds = mChildAccessibilityIds; |
| 999 | final int childrenSize = parcel.readInt(); |
| 1000 | for (int i = 0; i < childrenSize; i++) { |
| 1001 | final int childId = parcel.readInt(); |
| 1002 | childIds.put(i, childId); |
| 1003 | } |
| 1004 | |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 1005 | mBoundsInParent.top = parcel.readInt(); |
| 1006 | mBoundsInParent.bottom = parcel.readInt(); |
| 1007 | mBoundsInParent.left = parcel.readInt(); |
| 1008 | mBoundsInParent.right = parcel.readInt(); |
| 1009 | |
| 1010 | mBoundsInScreen.top = parcel.readInt(); |
| 1011 | mBoundsInScreen.bottom = parcel.readInt(); |
| 1012 | mBoundsInScreen.left = parcel.readInt(); |
| 1013 | mBoundsInScreen.right = parcel.readInt(); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 1014 | |
| 1015 | mActions = parcel.readInt(); |
| 1016 | |
| 1017 | mBooleanProperties = parcel.readInt(); |
| 1018 | |
| 1019 | mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); |
| 1020 | mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); |
| 1021 | mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); |
| 1022 | mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); |
| 1023 | } |
| 1024 | |
| 1025 | /** |
| 1026 | * Clears the state of this instance. |
| 1027 | */ |
| 1028 | private void clear() { |
| 1029 | mSealed = false; |
| 1030 | mConnection = null; |
| 1031 | mAccessibilityViewId = View.NO_ID; |
| 1032 | mParentAccessibilityViewId = View.NO_ID; |
Svetoslav Ganov | 35bfede | 2011-07-14 17:57:06 -0700 | [diff] [blame] | 1033 | mAccessibilityWindowId = View.NO_ID; |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 1034 | mChildAccessibilityIds.clear(); |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 1035 | mBoundsInParent.set(0, 0, 0, 0); |
| 1036 | mBoundsInScreen.set(0, 0, 0, 0); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 1037 | mBooleanProperties = 0; |
| 1038 | mPackageName = null; |
| 1039 | mClassName = null; |
| 1040 | mText = null; |
| 1041 | mContentDescription = null; |
| 1042 | mActions = 0; |
| 1043 | } |
| 1044 | |
| 1045 | /** |
| 1046 | * Gets the human readable action symbolic name. |
| 1047 | * |
| 1048 | * @param action The action. |
| 1049 | * @return The symbolic name. |
| 1050 | */ |
| 1051 | private static String getActionSymbolicName(int action) { |
Svetoslav Ganov | 38e8b4e | 2011-06-29 20:00:53 -0700 | [diff] [blame] | 1052 | switch (action) { |
| 1053 | case ACTION_FOCUS: |
| 1054 | return "ACTION_FOCUS"; |
| 1055 | case ACTION_CLEAR_FOCUS: |
| 1056 | return "ACTION_CLEAR_FOCUS"; |
| 1057 | case ACTION_SELECT: |
| 1058 | return "ACTION_SELECT"; |
| 1059 | case ACTION_CLEAR_SELECTION: |
| 1060 | return "ACTION_CLEAR_SELECTION"; |
| 1061 | default: |
| 1062 | throw new IllegalArgumentException("Unknown action: " + action); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 1063 | } |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 1064 | } |
| 1065 | |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 1066 | private boolean canPerformRequestOverConnection(int accessibilityViewId) { |
| 1067 | return (mAccessibilityWindowId != View.NO_ID |
| 1068 | && accessibilityViewId != View.NO_ID |
| 1069 | && mConnection != null); |
| 1070 | } |
| 1071 | |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 1072 | @Override |
Svetoslav Ganov | 8dffad6 | 2011-06-10 12:38:36 -0700 | [diff] [blame] | 1073 | public boolean equals(Object object) { |
| 1074 | if (this == object) { |
| 1075 | return true; |
| 1076 | } |
| 1077 | if (object == null) { |
| 1078 | return false; |
| 1079 | } |
| 1080 | if (getClass() != object.getClass()) { |
| 1081 | return false; |
| 1082 | } |
| 1083 | AccessibilityNodeInfo other = (AccessibilityNodeInfo) object; |
| 1084 | if (mAccessibilityViewId != other.mAccessibilityViewId) { |
| 1085 | return false; |
| 1086 | } |
| 1087 | if (mAccessibilityWindowId != other.mAccessibilityWindowId) { |
| 1088 | return false; |
| 1089 | } |
| 1090 | return true; |
| 1091 | } |
| 1092 | |
| 1093 | @Override |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 1094 | public int hashCode() { |
| 1095 | final int prime = 31; |
| 1096 | int result = 1; |
| 1097 | result = prime * result + mAccessibilityViewId; |
| 1098 | result = prime * result + mAccessibilityWindowId; |
| 1099 | return result; |
| 1100 | } |
| 1101 | |
| 1102 | @Override |
| 1103 | public String toString() { |
| 1104 | StringBuilder builder = new StringBuilder(); |
| 1105 | builder.append(super.toString()); |
| 1106 | |
| 1107 | if (DEBUG) { |
| 1108 | builder.append("; accessibilityId: " + mAccessibilityViewId); |
| 1109 | builder.append("; parentAccessibilityId: " + mParentAccessibilityViewId); |
| 1110 | SparseIntArray childIds = mChildAccessibilityIds; |
| 1111 | builder.append("; childAccessibilityIds: ["); |
| 1112 | for (int i = 0, count = childIds.size(); i < count; i++) { |
| 1113 | builder.append(childIds.valueAt(i)); |
| 1114 | if (i < count - 1) { |
| 1115 | builder.append(", "); |
| 1116 | } |
| 1117 | } |
| 1118 | builder.append("]"); |
| 1119 | } |
| 1120 | |
Svetoslav Ganov | eeee4d2 | 2011-06-10 20:51:30 -0700 | [diff] [blame] | 1121 | builder.append("; boundsInParent: " + mBoundsInParent); |
| 1122 | builder.append("; boundsInScreen: " + mBoundsInScreen); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 1123 | |
| 1124 | builder.append("; packageName: ").append(mPackageName); |
| 1125 | builder.append("; className: ").append(mClassName); |
| 1126 | builder.append("; text: ").append(mText); |
| 1127 | builder.append("; contentDescription: ").append(mContentDescription); |
| 1128 | |
| 1129 | builder.append("; checkable: ").append(isCheckable()); |
| 1130 | builder.append("; checked: ").append(isChecked()); |
| 1131 | builder.append("; focusable: ").append(isFocusable()); |
| 1132 | builder.append("; focused: ").append(isFocused()); |
| 1133 | builder.append("; selected: ").append(isSelected()); |
| 1134 | builder.append("; clickable: ").append(isClickable()); |
| 1135 | builder.append("; longClickable: ").append(isLongClickable()); |
| 1136 | builder.append("; enabled: ").append(isEnabled()); |
| 1137 | builder.append("; password: ").append(isPassword()); |
Svetoslav Ganov | a015617 | 2011-06-26 17:55:44 -0700 | [diff] [blame] | 1138 | builder.append("; scrollable: " + isScrollable()); |
Svetoslav Ganov | 8643aa0 | 2011-04-20 12:12:33 -0700 | [diff] [blame] | 1139 | |
| 1140 | builder.append("; ["); |
| 1141 | |
| 1142 | for (int actionBits = mActions; actionBits != 0;) { |
| 1143 | final int action = 1 << Integer.numberOfTrailingZeros(actionBits); |
| 1144 | actionBits &= ~action; |
| 1145 | builder.append(getActionSymbolicName(action)); |
| 1146 | if (actionBits != 0) { |
| 1147 | builder.append(", "); |
| 1148 | } |
| 1149 | } |
| 1150 | |
| 1151 | builder.append("]"); |
| 1152 | |
| 1153 | return builder.toString(); |
| 1154 | } |
| 1155 | |
| 1156 | /** |
| 1157 | * @see Parcelable.Creator |
| 1158 | */ |
| 1159 | public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR = |
| 1160 | new Parcelable.Creator<AccessibilityNodeInfo>() { |
| 1161 | public AccessibilityNodeInfo createFromParcel(Parcel parcel) { |
| 1162 | AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); |
| 1163 | info.initFromParcel(parcel); |
| 1164 | return info; |
| 1165 | } |
| 1166 | |
| 1167 | public AccessibilityNodeInfo[] newArray(int size) { |
| 1168 | return new AccessibilityNodeInfo[size]; |
| 1169 | } |
| 1170 | }; |
| 1171 | } |