diff options
Diffstat (limited to 'src/io/appium/droiddriver/scroll/SentinelStrategy.java')
-rw-r--r-- | src/io/appium/droiddriver/scroll/SentinelStrategy.java | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/src/io/appium/droiddriver/scroll/SentinelStrategy.java b/src/io/appium/droiddriver/scroll/SentinelStrategy.java new file mode 100644 index 0000000..d1583fd --- /dev/null +++ b/src/io/appium/droiddriver/scroll/SentinelStrategy.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2013 DroidDriver committers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.appium.droiddriver.scroll; + +import android.util.Log; + +import java.util.List; + +import io.appium.droiddriver.DroidDriver; +import io.appium.droiddriver.UiElement; +import io.appium.droiddriver.exceptions.ElementNotFoundException; +import io.appium.droiddriver.finders.By; +import io.appium.droiddriver.finders.Finder; +import io.appium.droiddriver.finders.Predicate; +import io.appium.droiddriver.finders.Predicates; +import io.appium.droiddriver.scroll.Direction.DirectionConverter; +import io.appium.droiddriver.scroll.Direction.LogicalDirection; +import io.appium.droiddriver.scroll.Direction.PhysicalDirection; +import io.appium.droiddriver.util.Logs; + +/** + * A {@link ScrollStepStrategy} that determines whether scrolling is possible + * based on a sentinel. + */ +public abstract class SentinelStrategy implements ScrollStepStrategy { + /** + * A {@link Finder} for sentinel. Note that unlike {@link Finder}, invisible + * UiElements are not skipped by default. + */ + public static abstract class Getter implements Finder { + protected final Predicate<? super UiElement> predicate; + + protected Getter() { + // Include invisible children by default. + this(null); + } + + protected Getter(Predicate<? super UiElement> predicate) { + this.predicate = predicate; + } + + /** + * Gets the sentinel, which must be an immediate child of {@code container} + * - not a descendant. Note sentinel may not exist if {@code container} has + * not finished updating. + */ + @Override + public UiElement find(UiElement container) { + UiElement sentinel = getSentinel(container.getChildren(predicate)); + if (sentinel == null) { + throw new ElementNotFoundException(this); + } + Logs.log(Log.INFO, "Found sentinel: " + sentinel); + return sentinel; + } + + + protected abstract UiElement getSentinel(List<? extends UiElement> children); + + @Override + public abstract String toString(); + } + + /** + * Returns the first child as the sentinel. + */ + public static final Getter FIRST_CHILD_GETTER = new Getter() { + @Override + protected UiElement getSentinel(List<? extends UiElement> children) { + return children.isEmpty() ? null : children.get(0); + } + + @Override + public String toString() { + return "FIRST_CHILD"; + } + }; + /** + * Returns the last child as the sentinel. + */ + public static final Getter LAST_CHILD_GETTER = new Getter() { + @Override + protected UiElement getSentinel(List<? extends UiElement> children) { + return children.isEmpty() ? null : children.get(children.size() - 1); + } + + @Override + public String toString() { + return "LAST_CHILD"; + } + }; + /** + * Returns the second last child as the sentinel. Useful when the activity + * always shows the last child as an anchor (for example a footer). + * <p> + * Sometimes uiautomatorviewer may not show the anchor as the last child, due + * to the reordering by layout described in {@link UiElement#getChildren}. + * This is not a problem with UiAutomationDriver because it sees the same as + * uiautomatorviewer does, but could be a problem with InstrumentationDriver. + * </p> + */ + public static final Getter SECOND_LAST_CHILD_GETTER = new Getter() { + @Override + protected UiElement getSentinel(List<? extends UiElement> children) { + return children.size() < 2 ? null : children.get(children.size() - 2); + } + + @Override + public String toString() { + return "SECOND_LAST_CHILD"; + } + }; + /** + * Returns the second child as the sentinel. Useful when the activity shows a + * fixed first child. + */ + public static final Getter SECOND_CHILD_GETTER = new Getter() { + @Override + protected UiElement getSentinel(List<? extends UiElement> children) { + return children.size() <= 1 ? null : children.get(1); + } + + @Override + public String toString() { + return "SECOND_CHILD"; + } + }; + + /** + * Decorates a {@link Getter} by adding another {@link Predicate}. + */ + public static class MorePredicateGetter extends Getter { + private final Getter original; + + public MorePredicateGetter(Getter original, Predicate<? super UiElement> extraPredicate) { + super(Predicates.allOf(original.predicate, extraPredicate)); + this.original = original; + } + + @Override + protected UiElement getSentinel(List<? extends UiElement> children) { + return original.getSentinel(children); + } + + @Override + public String toString() { + return predicate.toString() + " " + original; + } + } + + private final Getter backwardGetter; + private final Getter forwardGetter; + private final DirectionConverter directionConverter; + + protected SentinelStrategy(Getter backwardGetter, Getter forwardGetter, + DirectionConverter directionConverter) { + this.backwardGetter = backwardGetter; + this.forwardGetter = forwardGetter; + this.directionConverter = directionConverter; + } + + protected UiElement getSentinel(DroidDriver driver, Finder containerFinder, + PhysicalDirection direction) { + Logs.call(this, "getSentinel", driver, containerFinder, direction); + Finder sentinelFinder; + LogicalDirection logicalDirection = directionConverter.toLogicalDirection(direction); + if (logicalDirection == LogicalDirection.BACKWARD) { + sentinelFinder = By.chain(containerFinder, backwardGetter); + } else { + sentinelFinder = By.chain(containerFinder, forwardGetter); + } + return driver.on(sentinelFinder); + } + + @Override + public final DirectionConverter getDirectionConverter() { + return directionConverter; + } + + @Override + public void beginScrolling(DroidDriver driver, Finder containerFinder, Finder itemFinder, + PhysicalDirection direction) {} + + @Override + public void endScrolling(DroidDriver driver, Finder containerFinder, Finder itemFinder, + PhysicalDirection direction) {} + + @Override + public String toString() { + return String.format("{backwardGetter=%s, forwardGetter=%s}", backwardGetter, forwardGetter); + } + + @Override + public void doScroll(UiElement container, PhysicalDirection direction) { + container.scroll(direction); + } +} |