aboutsummaryrefslogtreecommitdiff
path: root/src/io/appium/droiddriver/scroll/SentinelStrategy.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/io/appium/droiddriver/scroll/SentinelStrategy.java')
-rw-r--r--src/io/appium/droiddriver/scroll/SentinelStrategy.java210
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);
+ }
+}