1 /* 2 * Copyright (C) 2012 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 package com.android.uiautomator.core; 17 18 import android.graphics.Rect; 19 import android.util.Log; 20 import android.view.accessibility.AccessibilityNodeInfo; 21 22 /** 23 * UiScrollable is a {@link UiCollection} and provides support for searching 24 * for items in scrollable layout elements. This class can be used with 25 * horizontally or vertically scrollable controls. 26 * @since API Level 16 27 */ 28 public class UiScrollable extends UiCollection { 29 private static final String LOG_TAG = UiScrollable.class.getSimpleName(); 30 31 // More steps slows the swipe and prevents contents from being flung too far 32 private static final int SCROLL_STEPS = 55; 33 34 private static final int FLING_STEPS = 5; 35 36 // Restrict a swipe's starting and ending points inside a 10% margin of the target 37 private static final double DEFAULT_SWIPE_DEADZONE_PCT = 0.1; 38 39 // Limits the number of swipes/scrolls performed during a search 40 private static int mMaxSearchSwipes = 30; 41 42 // Used in ScrollForward() and ScrollBackward() to determine swipe direction 43 private boolean mIsVerticalList = true; 44 45 private double mSwipeDeadZonePercentage = DEFAULT_SWIPE_DEADZONE_PCT; 46 47 /** 48 * Constructor. 49 * 50 * @param container a {@link UiSelector} selector to identify the scrollable 51 * layout element. 52 * @since API Level 16 53 */ UiScrollable(UiSelector container)54 public UiScrollable(UiSelector container) { 55 // wrap the container selector with container so that QueryController can handle 56 // this type of enumeration search accordingly 57 super(container); 58 } 59 60 /** 61 * Set the direction of swipes to be vertical when performing scroll actions. 62 * @return reference to itself 63 * @since API Level 16 64 */ setAsVerticalList()65 public UiScrollable setAsVerticalList() { 66 Tracer.trace(); 67 mIsVerticalList = true; 68 return this; 69 } 70 71 /** 72 * Set the direction of swipes to be horizontal when performing scroll actions. 73 * @return reference to itself 74 * @since API Level 16 75 */ setAsHorizontalList()76 public UiScrollable setAsHorizontalList() { 77 Tracer.trace(); 78 mIsVerticalList = false; 79 return this; 80 } 81 82 /** 83 * Used privately when performing swipe searches to decide if an element has become 84 * visible or not. 85 * 86 * @param selector 87 * @return true if found else false 88 * @since API Level 16 89 */ exists(UiSelector selector)90 protected boolean exists(UiSelector selector) { 91 if(getQueryController().findAccessibilityNodeInfo(selector) != null) { 92 return true; 93 } 94 return false; 95 } 96 97 /** 98 * Searches for a child element in the present scrollable container. 99 * The search first looks for a child element that matches the selector 100 * you provided, then looks for the content-description in its children elements. 101 * If both search conditions are fulfilled, the method returns a {@ link UiObject} 102 * representing the element matching the selector (not the child element in its 103 * subhierarchy containing the content-description). By default, this method performs a 104 * scroll search. 105 * See {@link #getChildByDescription(UiSelector, String, boolean)} 106 * 107 * @param childPattern {@link UiSelector} for a child in a scollable layout element 108 * @param text Content-description to find in the children of 109 * the <code>childPattern</code> match 110 * @return {@link UiObject} representing the child element that matches the search conditions 111 * @throws UiObjectNotFoundException 112 * @since API Level 16 113 */ 114 @Override getChildByDescription(UiSelector childPattern, String text)115 public UiObject getChildByDescription(UiSelector childPattern, String text) 116 throws UiObjectNotFoundException { 117 Tracer.trace(childPattern, text); 118 return getChildByDescription(childPattern, text, true); 119 } 120 121 /** 122 * Searches for a child element in the present scrollable container. 123 * The search first looks for a child element that matches the selector 124 * you provided, then looks for the content-description in its children elements. 125 * If both search conditions are fulfilled, the method returns a {@ link UiObject} 126 * representing the element matching the selector (not the child element in its 127 * subhierarchy containing the content-description). 128 * 129 * @param childPattern {@link UiSelector} for a child in a scollable layout element 130 * @param text Content-description to find in the children of 131 * the <code>childPattern</code> match (may be a partial match) 132 * @param allowScrollSearch set to true if scrolling is allowed 133 * @return {@link UiObject} representing the child element that matches the search conditions 134 * @throws UiObjectNotFoundException 135 * @since API Level 16 136 */ getChildByDescription(UiSelector childPattern, String text, boolean allowScrollSearch)137 public UiObject getChildByDescription(UiSelector childPattern, String text, 138 boolean allowScrollSearch) throws UiObjectNotFoundException { 139 Tracer.trace(childPattern, text, allowScrollSearch); 140 if (text != null) { 141 if (allowScrollSearch) { 142 scrollIntoView(new UiSelector().descriptionContains(text)); 143 } 144 return super.getChildByDescription(childPattern, text); 145 } 146 throw new UiObjectNotFoundException("for description= \"" + text + "\""); 147 } 148 149 /** 150 * Searches for a child element in the present scrollable container that 151 * matches the selector you provided. The search is performed without 152 * scrolling and only on visible elements. 153 * 154 * @param childPattern {@link UiSelector} for a child in a scollable layout element 155 * @param instance int number representing the occurance of 156 * a <code>childPattern</code> match 157 * @return {@link UiObject} representing the child element that matches the search conditions 158 * @since API Level 16 159 */ 160 @Override getChildByInstance(UiSelector childPattern, int instance)161 public UiObject getChildByInstance(UiSelector childPattern, int instance) 162 throws UiObjectNotFoundException { 163 Tracer.trace(childPattern, instance); 164 UiSelector patternSelector = UiSelector.patternBuilder(getSelector(), 165 UiSelector.patternBuilder(childPattern).instance(instance)); 166 return new UiObject(patternSelector); 167 } 168 169 /** 170 * Searches for a child element in the present scrollable 171 * container. The search first looks for a child element that matches the 172 * selector you provided, then looks for the text in its children elements. 173 * If both search conditions are fulfilled, the method returns a {@ link UiObject} 174 * representing the element matching the selector (not the child element in its 175 * subhierarchy containing the text). By default, this method performs a 176 * scroll search. 177 * See {@link #getChildByText(UiSelector, String, boolean)} 178 * 179 * @param childPattern {@link UiSelector} selector for a child in a scrollable layout element 180 * @param text String to find in the children of the <code>childPattern</code> match 181 * @return {@link UiObject} representing the child element that matches the search conditions 182 * @throws UiObjectNotFoundException 183 * @since API Level 16 184 */ 185 @Override getChildByText(UiSelector childPattern, String text)186 public UiObject getChildByText(UiSelector childPattern, String text) 187 throws UiObjectNotFoundException { 188 Tracer.trace(childPattern, text); 189 return getChildByText(childPattern, text, true); 190 } 191 192 /** 193 * Searches for a child element in the present scrollable container. The 194 * search first looks for a child element that matches the 195 * selector you provided, then looks for the text in its children elements. 196 * If both search conditions are fulfilled, the method returns a {@ link UiObject} 197 * representing the element matching the selector (not the child element in its 198 * subhierarchy containing the text). 199 * 200 * @param childPattern {@link UiSelector} selector for a child in a scrollable layout element 201 * @param text String to find in the children of the <code>childPattern</code> match 202 * @param allowScrollSearch set to true if scrolling is allowed 203 * @return {@link UiObject} representing the child element that matches the search conditions 204 * @throws UiObjectNotFoundException 205 * @since API Level 16 206 */ getChildByText(UiSelector childPattern, String text, boolean allowScrollSearch)207 public UiObject getChildByText(UiSelector childPattern, String text, boolean allowScrollSearch) 208 throws UiObjectNotFoundException { 209 Tracer.trace(childPattern, text, allowScrollSearch); 210 if (text != null) { 211 if (allowScrollSearch) { 212 scrollIntoView(new UiSelector().text(text)); 213 } 214 return super.getChildByText(childPattern, text); 215 } 216 throw new UiObjectNotFoundException("for text= \"" + text + "\""); 217 } 218 219 /** 220 * Performs a forward scroll action on the scrollable layout element until 221 * the content-description is found, or until swipe attempts have been exhausted. 222 * See {@link #setMaxSearchSwipes(int)} 223 * 224 * @param text content-description to find within the contents of this scrollable layout element. 225 * @return true if item is found; else, false 226 * @since API Level 16 227 */ scrollDescriptionIntoView(String text)228 public boolean scrollDescriptionIntoView(String text) throws UiObjectNotFoundException { 229 Tracer.trace(text); 230 return scrollIntoView(new UiSelector().description(text)); 231 } 232 233 /** 234 * Perform a forward scroll action to move through the scrollable layout element until 235 * a visible item that matches the {@link UiObject} is found. 236 * 237 * @param obj {@link UiObject} 238 * @return true if the item was found and now is in view else false 239 * @since API Level 16 240 */ scrollIntoView(UiObject obj)241 public boolean scrollIntoView(UiObject obj) throws UiObjectNotFoundException { 242 Tracer.trace(obj.getSelector()); 243 return scrollIntoView(obj.getSelector()); 244 } 245 246 /** 247 * Perform a scroll forward action to move through the scrollable layout 248 * element until a visible item that matches the selector is found. 249 * 250 * See {@link #scrollDescriptionIntoView(String)} and {@link #scrollTextIntoView(String)}. 251 * 252 * @param selector {@link UiSelector} selector 253 * @return true if the item was found and now is in view; else, false 254 * @since API Level 16 255 */ scrollIntoView(UiSelector selector)256 public boolean scrollIntoView(UiSelector selector) throws UiObjectNotFoundException { 257 Tracer.trace(selector); 258 // if we happen to be on top of the text we want then return here 259 UiSelector childSelector = getSelector().childSelector(selector); 260 if (exists(childSelector)) { 261 return (true); 262 } else { 263 // we will need to reset the search from the beginning to start search 264 scrollToBeginning(mMaxSearchSwipes); 265 if (exists(childSelector)) { 266 return (true); 267 } 268 for (int x = 0; x < mMaxSearchSwipes; x++) { 269 boolean scrolled = scrollForward(); 270 if(exists(childSelector)) { 271 return true; 272 } 273 if (!scrolled) { 274 return false; 275 } 276 } 277 } 278 return false; 279 } 280 281 /** 282 * Scrolls forward until the UiObject is fully visible in the scrollable container. 283 * Use this method to make sure that the child item's edges are not offscreen. 284 * 285 * @param childObject {@link UiObject} representing the child element 286 * @return true if the child element is already fully visible, or 287 * if the method scrolled successfully until the child became fully visible; 288 * otherwise, false if the attempt to scroll failed. 289 * @throws UiObjectNotFoundException 290 * @hide 291 */ ensureFullyVisible(UiObject childObject)292 public boolean ensureFullyVisible(UiObject childObject) throws UiObjectNotFoundException { 293 Rect actual = childObject.getBounds(); 294 Rect visible = childObject.getVisibleBounds(); 295 if (visible.width() * visible.height() == actual.width() * actual.height()) { 296 // area match, item fully visible 297 return true; 298 } 299 boolean shouldSwipeForward = false; 300 if (mIsVerticalList) { 301 // if list is vertical, matching top edge implies obscured bottom edge 302 // so we need to scroll list forward 303 shouldSwipeForward = actual.top == visible.top; 304 } else { 305 // if list is horizontal, matching left edge implies obscured right edge, 306 // so we need to scroll list forward 307 shouldSwipeForward = actual.left == visible.left; 308 } 309 if (mIsVerticalList) { 310 if (shouldSwipeForward) { 311 return swipeUp(10); 312 } else { 313 return swipeDown(10); 314 } 315 } else { 316 if (shouldSwipeForward) { 317 return swipeLeft(10); 318 } else { 319 return swipeRight(10); 320 } 321 } 322 } 323 324 /** 325 * Performs a forward scroll action on the scrollable layout element until 326 * the text you provided is visible, or until swipe attempts have been exhausted. 327 * See {@link #setMaxSearchSwipes(int)} 328 * 329 * @param text test to look for 330 * @return true if item is found; else, false 331 * @since API Level 16 332 */ scrollTextIntoView(String text)333 public boolean scrollTextIntoView(String text) throws UiObjectNotFoundException { 334 Tracer.trace(text); 335 return scrollIntoView(new UiSelector().text(text)); 336 } 337 338 /** 339 * Sets the maximum number of scrolls allowed when performing a 340 * scroll action in search of a child element. 341 * See {@link #getChildByDescription(UiSelector, String)} and 342 * {@link #getChildByText(UiSelector, String)}. 343 * 344 * @param swipes the number of search swipes to perform until giving up 345 * @return reference to itself 346 * @since API Level 16 347 */ setMaxSearchSwipes(int swipes)348 public UiScrollable setMaxSearchSwipes(int swipes) { 349 Tracer.trace(swipes); 350 mMaxSearchSwipes = swipes; 351 return this; 352 } 353 354 /** 355 * Gets the maximum number of scrolls allowed when performing a 356 * scroll action in search of a child element. 357 * See {@link #getChildByDescription(UiSelector, String)} and 358 * {@link #getChildByText(UiSelector, String)}. 359 * 360 * @return max the number of search swipes to perform until giving up 361 * @since API Level 16 362 */ getMaxSearchSwipes()363 public int getMaxSearchSwipes() { 364 Tracer.trace(); 365 return mMaxSearchSwipes; 366 } 367 368 /** 369 * Performs a forward fling with the default number of fling steps (5). 370 * If the swipe direction is set to vertical, then the swipes will be 371 * performed from bottom to top. If the swipe 372 * direction is set to horizontal, then the swipes will be performed from 373 * right to left. Make sure to take into account devices configured with 374 * right-to-left languages like Arabic and Hebrew. 375 * 376 * @return true if scrolled, false if can't scroll anymore 377 * @since API Level 16 378 */ flingForward()379 public boolean flingForward() throws UiObjectNotFoundException { 380 Tracer.trace(); 381 return scrollForward(FLING_STEPS); 382 } 383 384 /** 385 * Performs a forward scroll with the default number of scroll steps (55). 386 * If the swipe direction is set to vertical, 387 * then the swipes will be performed from bottom to top. If the swipe 388 * direction is set to horizontal, then the swipes will be performed from 389 * right to left. Make sure to take into account devices configured with 390 * right-to-left languages like Arabic and Hebrew. 391 * 392 * @return true if scrolled, false if can't scroll anymore 393 * @since API Level 16 394 */ scrollForward()395 public boolean scrollForward() throws UiObjectNotFoundException { 396 Tracer.trace(); 397 return scrollForward(SCROLL_STEPS); 398 } 399 400 /** 401 * Performs a forward scroll. If the swipe direction is set to vertical, 402 * then the swipes will be performed from bottom to top. If the swipe 403 * direction is set to horizontal, then the swipes will be performed from 404 * right to left. Make sure to take into account devices configured with 405 * right-to-left languages like Arabic and Hebrew. 406 * 407 * @param steps number of steps. Use this to control the speed of the scroll action 408 * @return true if scrolled, false if can't scroll anymore 409 * @since API Level 16 410 */ scrollForward(int steps)411 public boolean scrollForward(int steps) throws UiObjectNotFoundException { 412 Tracer.trace(steps); 413 Log.d(LOG_TAG, "scrollForward() on selector = " + getSelector()); 414 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 415 if(node == null) { 416 throw new UiObjectNotFoundException(getSelector().toString()); 417 } 418 Rect rect = new Rect(); 419 node.getBoundsInScreen(rect); 420 421 int downX = 0; 422 int downY = 0; 423 int upX = 0; 424 int upY = 0; 425 426 // scrolling is by default assumed vertically unless the object is explicitly 427 // set otherwise by setAsHorizontalContainer() 428 if(mIsVerticalList) { 429 int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage()); 430 // scroll vertically: swipe down -> up 431 downX = rect.centerX(); 432 downY = rect.bottom - swipeAreaAdjust; 433 upX = rect.centerX(); 434 upY = rect.top + swipeAreaAdjust; 435 } else { 436 int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage()); 437 // scroll horizontally: swipe right -> left 438 // TODO: Assuming device is not in right to left language 439 downX = rect.right - swipeAreaAdjust; 440 downY = rect.centerY(); 441 upX = rect.left + swipeAreaAdjust; 442 upY = rect.centerY(); 443 } 444 return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps); 445 } 446 447 /** 448 * Performs a backwards fling action with the default number of fling 449 * steps (5). If the swipe direction is set to vertical, 450 * then the swipe will be performed from top to bottom. If the swipe 451 * direction is set to horizontal, then the swipes will be performed from 452 * left to right. Make sure to take into account devices configured with 453 * right-to-left languages like Arabic and Hebrew. 454 * 455 * @return true if scrolled, and false if can't scroll anymore 456 * @since API Level 16 457 */ flingBackward()458 public boolean flingBackward() throws UiObjectNotFoundException { 459 Tracer.trace(); 460 return scrollBackward(FLING_STEPS); 461 } 462 463 /** 464 * Performs a backward scroll with the default number of scroll steps (55). 465 * If the swipe direction is set to vertical, 466 * then the swipes will be performed from top to bottom. If the swipe 467 * direction is set to horizontal, then the swipes will be performed from 468 * left to right. Make sure to take into account devices configured with 469 * right-to-left languages like Arabic and Hebrew. 470 * 471 * @return true if scrolled, and false if can't scroll anymore 472 * @since API Level 16 473 */ scrollBackward()474 public boolean scrollBackward() throws UiObjectNotFoundException { 475 Tracer.trace(); 476 return scrollBackward(SCROLL_STEPS); 477 } 478 479 /** 480 * Performs a backward scroll. If the swipe direction is set to vertical, 481 * then the swipes will be performed from top to bottom. If the swipe 482 * direction is set to horizontal, then the swipes will be performed from 483 * left to right. Make sure to take into account devices configured with 484 * right-to-left languages like Arabic and Hebrew. 485 * 486 * @param steps number of steps. Use this to control the speed of the scroll action. 487 * @return true if scrolled, false if can't scroll anymore 488 * @since API Level 16 489 */ scrollBackward(int steps)490 public boolean scrollBackward(int steps) throws UiObjectNotFoundException { 491 Tracer.trace(steps); 492 Log.d(LOG_TAG, "scrollBackward() on selector = " + getSelector()); 493 AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT); 494 if (node == null) { 495 throw new UiObjectNotFoundException(getSelector().toString()); 496 } 497 Rect rect = new Rect(); 498 node.getBoundsInScreen(rect); 499 500 int downX = 0; 501 int downY = 0; 502 int upX = 0; 503 int upY = 0; 504 505 // scrolling is by default assumed vertically unless the object is explicitly 506 // set otherwise by setAsHorizontalContainer() 507 if(mIsVerticalList) { 508 int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage()); 509 Log.d(LOG_TAG, "scrollToBegining() using vertical scroll"); 510 // scroll vertically: swipe up -> down 511 downX = rect.centerX(); 512 downY = rect.top + swipeAreaAdjust; 513 upX = rect.centerX(); 514 upY = rect.bottom - swipeAreaAdjust; 515 } else { 516 int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage()); 517 Log.d(LOG_TAG, "scrollToBegining() using hotizontal scroll"); 518 // scroll horizontally: swipe left -> right 519 // TODO: Assuming device is not in right to left language 520 downX = rect.left + swipeAreaAdjust; 521 downY = rect.centerY(); 522 upX = rect.right - swipeAreaAdjust; 523 upY = rect.centerY(); 524 } 525 return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps); 526 } 527 528 /** 529 * Scrolls to the beginning of a scrollable layout element. The beginning 530 * can be at the top-most edge in the case of vertical controls, or the 531 * left-most edge for horizontal controls. Make sure to take into account 532 * devices configured with right-to-left languages like Arabic and Hebrew. 533 * 534 * @param steps use steps to control the speed, so that it may be a scroll, or fling 535 * @return true on scrolled else false 536 * @since API Level 16 537 */ scrollToBeginning(int maxSwipes, int steps)538 public boolean scrollToBeginning(int maxSwipes, int steps) throws UiObjectNotFoundException { 539 Tracer.trace(maxSwipes, steps); 540 Log.d(LOG_TAG, "scrollToBeginning() on selector = " + getSelector()); 541 // protect against potential hanging and return after preset attempts 542 for(int x = 0; x < maxSwipes; x++) { 543 if(!scrollBackward(steps)) { 544 break; 545 } 546 } 547 return true; 548 } 549 550 /** 551 * Scrolls to the beginning of a scrollable layout element. The beginning 552 * can be at the top-most edge in the case of vertical controls, or the 553 * left-most edge for horizontal controls. Make sure to take into account 554 * devices configured with right-to-left languages like Arabic and Hebrew. 555 * 556 * @param maxSwipes 557 * @return true on scrolled else false 558 * @since API Level 16 559 */ scrollToBeginning(int maxSwipes)560 public boolean scrollToBeginning(int maxSwipes) throws UiObjectNotFoundException { 561 Tracer.trace(maxSwipes); 562 return scrollToBeginning(maxSwipes, SCROLL_STEPS); 563 } 564 565 /** 566 * Performs a fling gesture to reach the beginning of a scrollable layout element. 567 * The beginning can be at the top-most edge in the case of vertical controls, or 568 * the left-most edge for horizontal controls. Make sure to take into 569 * account devices configured with right-to-left languages like Arabic and Hebrew. 570 * 571 * @param maxSwipes 572 * @return true on scrolled else false 573 * @since API Level 16 574 */ flingToBeginning(int maxSwipes)575 public boolean flingToBeginning(int maxSwipes) throws UiObjectNotFoundException { 576 Tracer.trace(maxSwipes); 577 return scrollToBeginning(maxSwipes, FLING_STEPS); 578 } 579 580 /** 581 * Scrolls to the end of a scrollable layout element. The end can be at the 582 * bottom-most edge in the case of vertical controls, or the right-most edge for 583 * horizontal controls. Make sure to take into account devices configured with 584 * right-to-left languages like Arabic and Hebrew. 585 * 586 * @param steps use steps to control the speed, so that it may be a scroll, or fling 587 * @return true on scrolled else false 588 * @since API Level 16 589 */ scrollToEnd(int maxSwipes, int steps)590 public boolean scrollToEnd(int maxSwipes, int steps) throws UiObjectNotFoundException { 591 Tracer.trace(maxSwipes, steps); 592 // protect against potential hanging and return after preset attempts 593 for(int x = 0; x < maxSwipes; x++) { 594 if(!scrollForward(steps)) { 595 break; 596 } 597 } 598 return true; 599 } 600 601 /** 602 * Scrolls to the end of a scrollable layout element. The end can be at the 603 * bottom-most edge in the case of vertical controls, or the right-most edge for 604 * horizontal controls. Make sure to take into account devices configured with 605 * right-to-left languages like Arabic and Hebrew. 606 * 607 * @param maxSwipes 608 * @return true on scrolled, else false 609 * @since API Level 16 610 */ scrollToEnd(int maxSwipes)611 public boolean scrollToEnd(int maxSwipes) throws UiObjectNotFoundException { 612 Tracer.trace(maxSwipes); 613 return scrollToEnd(maxSwipes, SCROLL_STEPS); 614 } 615 616 /** 617 * Performs a fling gesture to reach the end of a scrollable layout element. 618 * The end can be at the bottom-most edge in the case of vertical controls, or 619 * the right-most edge for horizontal controls. Make sure to take into 620 * account devices configured with right-to-left languages like Arabic and Hebrew. 621 * 622 * @param maxSwipes 623 * @return true on scrolled, else false 624 * @since API Level 16 625 */ flingToEnd(int maxSwipes)626 public boolean flingToEnd(int maxSwipes) throws UiObjectNotFoundException { 627 Tracer.trace(maxSwipes); 628 return scrollToEnd(maxSwipes, FLING_STEPS); 629 } 630 631 /** 632 * Returns the percentage of a widget's size that's considered as a no-touch 633 * zone when swiping. The no-touch zone is set as a percentage of a widget's total 634 * width or height, denoting a margin around the swipable area of the widget. 635 * Swipes must start and end inside this margin. This is important when the 636 * widget being swiped may not respond to the swipe if started at a point 637 * too near to the edge. The default is 10% from either edge. 638 * 639 * @return a value between 0 and 1 640 * @since API Level 16 641 */ getSwipeDeadZonePercentage()642 public double getSwipeDeadZonePercentage() { 643 Tracer.trace(); 644 return mSwipeDeadZonePercentage; 645 } 646 647 /** 648 * Sets the percentage of a widget's size that's considered as no-touch 649 * zone when swiping. 650 * The no-touch zone is set as percentage of a widget's total width or height, 651 * denoting a margin around the swipable area of the widget. Swipes must 652 * always start and end inside this margin. This is important when the 653 * widget being swiped may not respond to the swipe if started at a point 654 * too near to the edge. The default is 10% from either edge. 655 * 656 * @param swipeDeadZonePercentage is a value between 0 and 1 657 * @return reference to itself 658 * @since API Level 16 659 */ setSwipeDeadZonePercentage(double swipeDeadZonePercentage)660 public UiScrollable setSwipeDeadZonePercentage(double swipeDeadZonePercentage) { 661 Tracer.trace(swipeDeadZonePercentage); 662 mSwipeDeadZonePercentage = swipeDeadZonePercentage; 663 return this; 664 } 665 } 666