1/* 2 * Copyright (C) 2015 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 17package android.support.v4.view.accessibility; 18 19import android.graphics.Rect; 20import android.os.Build; 21 22/** 23 * Helper for accessing {@link android.view.accessibility.AccessibilityWindowInfo} 24 * introduced after API level 4 in a backwards compatible fashion. 25 */ 26public class AccessibilityWindowInfoCompat { 27 28 private static interface AccessibilityWindowInfoImpl { 29 public Object obtain(); 30 public Object obtain(Object info); 31 public int getType(Object info); 32 public int getLayer(Object info); 33 public Object getRoot(Object info); 34 public Object getParent(Object info); 35 public int getId(Object info); 36 public void getBoundsInScreen(Object info, Rect outBounds); 37 public boolean isActive(Object info); 38 public boolean isFocused(Object info); 39 public boolean isAccessibilityFocused(Object info); 40 public int getChildCount(Object info); 41 public Object getChild(Object info, int index); 42 public CharSequence getTitle(Object info); 43 public Object getAnchor(Object info); 44 public void recycle(Object info); 45 } 46 47 private static class AccessibilityWindowInfoStubImpl implements AccessibilityWindowInfoImpl { 48 49 @Override 50 public Object obtain() { 51 return null; 52 } 53 54 @Override 55 public Object obtain(Object info) { 56 return null; 57 } 58 59 @Override 60 public int getType(Object info) { 61 return UNDEFINED; 62 } 63 64 @Override 65 public int getLayer(Object info) { 66 return UNDEFINED; 67 } 68 69 @Override 70 public Object getRoot(Object info) { 71 return null; 72 } 73 74 @Override 75 public Object getParent(Object info) { 76 return null; 77 } 78 79 @Override 80 public int getId(Object info) { 81 return UNDEFINED; 82 } 83 84 @Override 85 public void getBoundsInScreen(Object info, Rect outBounds) { 86 } 87 88 @Override 89 public boolean isActive(Object info) { 90 return true; 91 } 92 93 @Override 94 public boolean isFocused(Object info) { 95 return true; 96 } 97 98 @Override 99 public boolean isAccessibilityFocused(Object info) { 100 return true; 101 } 102 103 @Override 104 public int getChildCount(Object info) { 105 return 0; 106 } 107 108 @Override 109 public Object getChild(Object info, int index) { 110 return null; 111 } 112 113 @Override 114 public void recycle(Object info) { 115 } 116 117 @Override 118 public CharSequence getTitle(Object info) { 119 return null; 120 } 121 122 @Override 123 public Object getAnchor(Object info) { 124 return null; 125 } 126 } 127 128 private static class AccessibilityWindowInfoApi21Impl extends AccessibilityWindowInfoStubImpl { 129 @Override 130 public Object obtain() { 131 return AccessibilityWindowInfoCompatApi21.obtain(); 132 } 133 134 @Override 135 public Object obtain(Object info) { 136 return AccessibilityWindowInfoCompatApi21.obtain(info); 137 } 138 139 @Override 140 public int getType(Object info) { 141 return AccessibilityWindowInfoCompatApi21.getType(info); 142 } 143 144 @Override 145 public int getLayer(Object info) { 146 return AccessibilityWindowInfoCompatApi21.getLayer(info); 147 } 148 149 @Override 150 public Object getRoot(Object info) { 151 return AccessibilityWindowInfoCompatApi21.getRoot(info); 152 } 153 154 @Override 155 public Object getParent(Object info) { 156 return AccessibilityWindowInfoCompatApi21.getParent(info); 157 } 158 159 @Override 160 public int getId(Object info) { 161 return AccessibilityWindowInfoCompatApi21.getId(info); 162 } 163 164 @Override 165 public void getBoundsInScreen(Object info, Rect outBounds) { 166 AccessibilityWindowInfoCompatApi21.getBoundsInScreen(info, outBounds); 167 } 168 169 @Override 170 public boolean isActive(Object info) { 171 return AccessibilityWindowInfoCompatApi21.isActive(info); 172 } 173 174 @Override 175 public boolean isFocused(Object info) { 176 return AccessibilityWindowInfoCompatApi21.isFocused(info); 177 } 178 179 @Override 180 public boolean isAccessibilityFocused(Object info) { 181 return AccessibilityWindowInfoCompatApi21.isAccessibilityFocused(info); 182 } 183 184 @Override 185 public int getChildCount(Object info) { 186 return AccessibilityWindowInfoCompatApi21.getChildCount(info); 187 } 188 189 @Override 190 public Object getChild(Object info, int index) { 191 return AccessibilityWindowInfoCompatApi21.getChild(info, index); 192 } 193 194 @Override 195 public void recycle(Object info) { 196 AccessibilityWindowInfoCompatApi21.recycle(info); 197 } 198 } 199 200 private static class AccessibilityWindowInfoApi24Impl extends AccessibilityWindowInfoApi21Impl { 201 @Override 202 public CharSequence getTitle(Object info) { 203 return AccessibilityWindowInfoCompatApi24.getTitle(info); 204 } 205 206 @Override 207 public Object getAnchor(Object info) { 208 return AccessibilityWindowInfoCompatApi24.getAnchor(info); 209 } 210 } 211 212 static { 213 if (Build.VERSION.SDK_INT >= 24) { 214 IMPL = new AccessibilityWindowInfoApi24Impl(); 215 } else if (Build.VERSION.SDK_INT >= 21) { 216 IMPL = new AccessibilityWindowInfoApi21Impl(); 217 } else { 218 IMPL = new AccessibilityWindowInfoStubImpl(); 219 } 220 } 221 222 private static final AccessibilityWindowInfoImpl IMPL; 223 private Object mInfo; 224 225 private static final int UNDEFINED = -1; 226 227 /** 228 * Window type: This is an application window. Such a window shows UI for 229 * interacting with an application. 230 */ 231 public static final int TYPE_APPLICATION = 1; 232 233 /** 234 * Window type: This is an input method window. Such a window shows UI for 235 * inputting text such as keyboard, suggestions, etc. 236 */ 237 public static final int TYPE_INPUT_METHOD = 2; 238 239 /** 240 * Window type: This is an system window. Such a window shows UI for 241 * interacting with the system. 242 */ 243 public static final int TYPE_SYSTEM = 3; 244 245 /** 246 * Window type: Windows that are overlaid <em>only</em> by an {@link 247 * android.accessibilityservice.AccessibilityService} for interception of 248 * user interactions without changing the windows an accessibility service 249 * can introspect. In particular, an accessibility service can introspect 250 * only windows that a sighted user can interact with which they can touch 251 * these windows or can type into these windows. For example, if there 252 * is a full screen accessibility overlay that is touchable, the windows 253 * below it will be introspectable by an accessibility service regardless 254 * they are covered by a touchable window. 255 */ 256 public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; 257 258 /** 259 * Creates a wrapper for info implementation. 260 * 261 * @param object The info to wrap. 262 * @return A wrapper for if the object is not null, null otherwise. 263 */ 264 static AccessibilityWindowInfoCompat wrapNonNullInstance(Object object) { 265 if (object != null) { 266 return new AccessibilityWindowInfoCompat(object); 267 } 268 return null; 269 } 270 271 private AccessibilityWindowInfoCompat(Object info) { 272 mInfo = info; 273 } 274 275 /** 276 * Gets the type of the window. 277 * 278 * @return The type. 279 * 280 * @see #TYPE_APPLICATION 281 * @see #TYPE_INPUT_METHOD 282 * @see #TYPE_SYSTEM 283 * @see #TYPE_ACCESSIBILITY_OVERLAY 284 */ 285 public int getType() { 286 return IMPL.getType(mInfo); 287 } 288 289 /** 290 * Gets the layer which determines the Z-order of the window. Windows 291 * with greater layer appear on top of windows with lesser layer. 292 * 293 * @return The window layer. 294 */ 295 public int getLayer() { 296 return IMPL.getLayer(mInfo); 297 } 298 299 /** 300 * Gets the root node in the window's hierarchy. 301 * 302 * @return The root node. 303 */ 304 public AccessibilityNodeInfoCompat getRoot() { 305 return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getRoot(mInfo)); 306 } 307 308 /** 309 * Gets the parent window if such. 310 * 311 * @return The parent window. 312 */ 313 public AccessibilityWindowInfoCompat getParent() { 314 return wrapNonNullInstance(IMPL.getParent(mInfo)); 315 } 316 317 /** 318 * Gets the unique window id. 319 * 320 * @return windowId The window id. 321 */ 322 public int getId() { 323 return IMPL.getId(mInfo); 324 } 325 326 /** 327 * Gets the bounds of this window in the screen. 328 * 329 * @param outBounds The out window bounds. 330 */ 331 public void getBoundsInScreen(Rect outBounds) { 332 IMPL.getBoundsInScreen(mInfo, outBounds); 333 } 334 335 /** 336 * Gets if this window is active. An active window is the one 337 * the user is currently touching or the window has input focus 338 * and the user is not touching any window. 339 * 340 * @return Whether this is the active window. 341 */ 342 public boolean isActive() { 343 return IMPL.isActive(mInfo); 344 } 345 346 /** 347 * Gets if this window has input focus. 348 * 349 * @return Whether has input focus. 350 */ 351 public boolean isFocused() { 352 return IMPL.isFocused(mInfo); 353 } 354 355 /** 356 * Gets if this window has accessibility focus. 357 * 358 * @return Whether has accessibility focus. 359 */ 360 public boolean isAccessibilityFocused() { 361 return IMPL.isAccessibilityFocused(mInfo); 362 } 363 364 /** 365 * Gets the number of child windows. 366 * 367 * @return The child count. 368 */ 369 public int getChildCount() { 370 return IMPL.getChildCount(mInfo); 371 } 372 373 /** 374 * Gets the child window at a given index. 375 * 376 * @param index The index. 377 * @return The child. 378 */ 379 public AccessibilityWindowInfoCompat getChild(int index) { 380 return wrapNonNullInstance(IMPL.getChild(mInfo, index)); 381 } 382 383 /** 384 * Gets the title of the window. 385 * 386 * @return The title of the window, or the application label for the window if no title was 387 * explicitly set, or {@code null} if neither is available. 388 */ 389 public CharSequence getTitle() { 390 return IMPL.getTitle(mInfo); 391 } 392 393 /** 394 * Gets the node that anchors this window to another. 395 * 396 * @return The anchor node, or {@code null} if none exists. 397 */ 398 public AccessibilityNodeInfoCompat getAnchor() { 399 return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getAnchor(mInfo)); 400 } 401 402 /** 403 * Returns a cached instance if such is available or a new one is 404 * created. 405 * 406 * @return An instance. 407 */ 408 public static AccessibilityWindowInfoCompat obtain() { 409 return wrapNonNullInstance(IMPL.obtain()); 410 } 411 412 /** 413 * Returns a cached instance if such is available or a new one is 414 * created. The returned instance is initialized from the given 415 * <code>info</code>. 416 * 417 * @param info The other info. 418 * @return An instance. 419 */ 420 public static AccessibilityWindowInfoCompat obtain(AccessibilityWindowInfoCompat info) { 421 return wrapNonNullInstance(IMPL.obtain(info.mInfo)); 422 } 423 424 /** 425 * Return an instance back to be reused. 426 * <p> 427 * <strong>Note:</strong> You must not touch the object after calling this function. 428 * </p> 429 * 430 * @throws IllegalStateException If the info is already recycled. 431 */ 432 public void recycle() { 433 IMPL.recycle(mInfo); 434 } 435 436 @Override 437 public int hashCode() { 438 return (mInfo == null) ? 0 : mInfo.hashCode(); 439 } 440 441 @Override 442 public boolean equals(Object obj) { 443 if (this == obj) { 444 return true; 445 } 446 if (obj == null) { 447 return false; 448 } 449 if (getClass() != obj.getClass()) { 450 return false; 451 } 452 AccessibilityWindowInfoCompat other = (AccessibilityWindowInfoCompat) obj; 453 if (mInfo == null) { 454 if (other.mInfo != null) { 455 return false; 456 } 457 } else if (!mInfo.equals(other.mInfo)) { 458 return false; 459 } 460 return true; 461 } 462 463 @Override 464 public String toString() { 465 StringBuilder builder = new StringBuilder(); 466 Rect bounds = new Rect(); 467 getBoundsInScreen(bounds); 468 builder.append("AccessibilityWindowInfo["); 469 builder.append("id=").append(getId()); 470 builder.append(", type=").append(typeToString(getType())); 471 builder.append(", layer=").append(getLayer()); 472 builder.append(", bounds=").append(bounds); 473 builder.append(", focused=").append(isFocused()); 474 builder.append(", active=").append(isActive()); 475 builder.append(", hasParent=").append(getParent() != null); 476 builder.append(", hasChildren=").append(getChildCount() > 0); 477 builder.append(']'); 478 return builder.toString(); 479 } 480 481 private static String typeToString(int type) { 482 switch (type) { 483 case TYPE_APPLICATION: { 484 return "TYPE_APPLICATION"; 485 } 486 case TYPE_INPUT_METHOD: { 487 return "TYPE_INPUT_METHOD"; 488 } 489 case TYPE_SYSTEM: { 490 return "TYPE_SYSTEM"; 491 } 492 case TYPE_ACCESSIBILITY_OVERLAY: { 493 return "TYPE_ACCESSIBILITY_OVERLAY"; 494 } 495 default: 496 return "<UNKNOWN>"; 497 } 498 } 499} 500