blob: 746ad6d068e9c24b79c8001c484762312ce47dac [file] [log] [blame]
/*
* 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 com.google.android.droiddriver.scroll;
import android.util.Log;
import com.google.android.droiddriver.DroidDriver;
import com.google.android.droiddriver.Poller;
import com.google.android.droiddriver.UiElement;
import com.google.android.droiddriver.actions.ScrollDirection;
import com.google.android.droiddriver.exceptions.ElementNotFoundException;
import com.google.android.droiddriver.exceptions.TimeoutException;
import com.google.android.droiddriver.finders.Finder;
import com.google.android.droiddriver.scroll.Direction.Axis;
import com.google.android.droiddriver.util.Logs;
/**
* A {@link Scroller} that looks for the desired child in the current shown
* content of the parent, otherwise scrolls the parent one step at a time and
* look again, until we cannot scroll any more. A {@link SentinelStrategy} is
* used to determine whether more scrolling is possible.
* <p>
* This algorithm is needed unless the DroidDriver implementation supports
* directly jumping to the child.
*/
public class SentinelScroller implements Scroller {
private final int maxScrolls;
private final long perScrollTimeoutMillis;
private final Axis axis;
private final SentinelStrategy sentinelStrategy;
/**
* @param maxScrolls the maximum number of scrolls. It should be large enough
* to allow any reasonable list size
* @param perScrollTimeoutMillis the timeout in millis that we poll for the
* child after each scroll
* @param axis the axis this scroller can scroll
*/
public SentinelScroller(int maxScrolls, long perScrollTimeoutMillis, Axis axis,
SentinelStrategy sentinelStrategy) {
this.maxScrolls = maxScrolls;
this.perScrollTimeoutMillis = perScrollTimeoutMillis;
this.axis = axis;
this.sentinelStrategy = sentinelStrategy;
}
/**
* Constructs with default 100 maxScrolls, 1 second for
* perScrollTimeoutMillis, vertical axis.
*/
public SentinelScroller(SentinelStrategy sentinelStrategy) {
this(100, 1000, Axis.VERTICAL, sentinelStrategy);
}
@Override
public UiElement scrollTo(DroidDriver driver, Finder parentFinder, Finder childFinder,
ScrollDirection direction) {
Logs.call(this, "scrollTo", driver, parentFinder, childFinder, direction);
// TODO: enforce childFinder is relative to parentFinder.
// Combine with parentFinder to make childFinder absolute
// childFinder = By.chain(parentFinder, childFinder);
int i = 0;
for (; i <= maxScrolls; i++) {
try {
return driver.getPoller().pollFor(driver, childFinder, Poller.EXISTS,
perScrollTimeoutMillis);
} catch (TimeoutException e) {
if (i < maxScrolls && !sentinelStrategy.scroll(driver, parentFinder, direction)) {
break;
}
}
}
ElementNotFoundException exception = new ElementNotFoundException(childFinder);
if (i == maxScrolls) {
// This is often a program error -- maxScrolls is a safety net; we should
// have either found childFinder, or stopped to scroll b/c of reaching the
// end. If maxScrolls is reasonably large, sentinelStrategy must be wrong.
Logs.logfmt(Log.WARN, exception, "Scrolled %s %d times; sentinelStrategy=%s", parentFinder,
maxScrolls, sentinelStrategy);
}
throw exception;
}
@Override
public UiElement scrollTo(DroidDriver driver, Finder parentFinder, Finder childFinder) {
// TODO: start searching from beginning instead of the current location.
for (ScrollDirection direction : axis.getDirections()) {
try {
return scrollTo(driver, parentFinder, childFinder, direction);
} catch (ElementNotFoundException e) {
// try another direction
}
}
throw new ElementNotFoundException(childFinder);
}
}