1f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin/*
2f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin * Copyright (C) 2013 DroidDriver committers
3f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin *
4f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin * Licensed under the Apache License, Version 2.0 (the "License");
5f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin * you may not use this file except in compliance with the License.
6f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin * You may obtain a copy of the License at
7f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin *
8f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin *      http://www.apache.org/licenses/LICENSE-2.0
9f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin *
10f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin * Unless required by applicable law or agreed to in writing, software
11f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin * distributed under the License is distributed on an "AS IS" BASIS,
12f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin * See the License for the specific language governing permissions and
14f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin * limitations under the License.
15f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin */
16f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jinpackage com.google.android.droiddriver.scroll;
17f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
18f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jinimport android.util.Log;
19f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
20f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jinimport com.google.android.droiddriver.DroidDriver;
21f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jinimport com.google.android.droiddriver.UiElement;
22f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jinimport com.google.android.droiddriver.exceptions.ElementNotFoundException;
23dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jinimport com.google.android.droiddriver.finders.By;
24f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jinimport com.google.android.droiddriver.finders.Finder;
25b5194043e9f0a1319dc7251f829febab3c76e277Kevin Jinimport com.google.android.droiddriver.scroll.Direction.DirectionConverter;
2629d66eeee5d30f7db747cceeb84defec961b4125Kevin Jinimport com.google.android.droiddriver.scroll.Direction.PhysicalDirection;
27f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jinimport com.google.android.droiddriver.util.Logs;
2817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport com.google.android.droiddriver.util.Strings;
29f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
30f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin/**
31f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin * Determines whether scrolling is possible by checking whether the sentinel
320319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin * child is updated after scrolling. Use this when {@link UiElement#getChildren}
330319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin * is not reliable. This can happen, for instance, when UiAutomationDriver is
340319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin * used, which skips invisible children, or in the case of dynamic list, which
350319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin * shows more items when scrolling beyond the end.
36f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin */
37e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jinpublic class DynamicSentinelStrategy extends SentinelStrategy {
38f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
39f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  /**
40f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin   * Interface for determining whether sentinel is updated.
41f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin   */
42f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  public static interface IsUpdatedStrategy {
43f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    /**
44f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     * Returns whether {@code newSentinel} is updated from {@code oldSentinel}.
45f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     */
46f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    boolean isSentinelUpdated(UiElement newSentinel, UiElement oldSentinel);
47f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
48f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    /**
49f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     * {@inheritDoc}
50f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     *
51f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     * <p>
52f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     * It is recommended that this method return a description to help
53f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     * debugging.
54f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     */
55f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    @Override
56f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    String toString();
57f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  }
58f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
59f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  /**
60f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin   * Determines whether the sentinel is updated by checking a single unique
610319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin   * String attribute of a descendant element of the sentinel (or itself).
62f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin   */
63f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  public static abstract class SingleStringUpdated implements IsUpdatedStrategy {
64f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    private final Finder uniqueStringFinder;
65f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
66f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    /**
67f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     * @param uniqueStringFinder a Finder relative to the sentinel that finds
680319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin     *        its descendant or self which contains a unique String.
69f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     */
70f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    public SingleStringUpdated(Finder uniqueStringFinder) {
71f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      this.uniqueStringFinder = uniqueStringFinder;
72f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    }
73f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
74f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    /**
750319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin     * @param uniqueStringElement the descendant or self that contains the
760319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin     *        unique String
77f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     * @return the unique String
78f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin     */
790319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin    protected abstract String getUniqueString(UiElement uniqueStringElement);
80f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
81f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    private String getUniqueStringFromSentinel(UiElement sentinel) {
82f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      try {
83f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin        return getUniqueString(uniqueStringFinder.find(sentinel));
84f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      } catch (ElementNotFoundException e) {
85f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin        return null;
86f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      }
87f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    }
88f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
89f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    @Override
90f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    public boolean isSentinelUpdated(UiElement newSentinel, UiElement oldSentinel) {
9170e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin      // If the sentinel moved, scrolling has some effect. This is both an
9270e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin      // optimization - getBounds is cheaper than find - and necessary in
9370e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin      // certain cases, e.g. user is looking for a sibling of the unique string;
9470e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin      // the scroll is close to the end therefore the unique string does not
9570e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin      // change, but the target could be revealed.
9670e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin      if (!newSentinel.getBounds().equals(oldSentinel.getBounds())) {
9770e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin        return true;
9870e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin      }
9970e34108e0fc19277e642aef3b36b65b8e254899Kevin Jin
100f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      String newString = getUniqueStringFromSentinel(newSentinel);
101a5bb27d69e8501b7c8321b838646d0b8f6fa0d05Kevin Jin      // A legitimate case for newString being null is when newSentinel is
102a5bb27d69e8501b7c8321b838646d0b8f6fa0d05Kevin Jin      // partially shown. We return true to allow further scrolling. But program
1039c92f46280cf3943701e75349833c68b584992e2Kevin Jin      // error could also cause this, e.g. a bad choice of Getter, which
104a5bb27d69e8501b7c8321b838646d0b8f6fa0d05Kevin Jin      // results in unnecessary scroll actions that have no visual effect. This
105a5bb27d69e8501b7c8321b838646d0b8f6fa0d05Kevin Jin      // log helps troubleshooting in the latter case.
106f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      if (newString == null) {
107a5bb27d69e8501b7c8321b838646d0b8f6fa0d05Kevin Jin        Logs.logfmt(Log.WARN, "Unique String is null: sentinel=%s, uniqueStringFinder=%s",
108a5bb27d69e8501b7c8321b838646d0b8f6fa0d05Kevin Jin            newSentinel, uniqueStringFinder);
109f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin        return true;
110f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      }
111f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      if (newString.equals(getUniqueStringFromSentinel(oldSentinel))) {
112f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin        Logs.log(Log.INFO, "Unique String is not updated: " + newString);
113f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin        return false;
114f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      }
115f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      return true;
116f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    }
117f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
118f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    @Override
119f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    public String toString() {
12017342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      return Strings.toStringHelper(this).addValue(uniqueStringFinder).toString();
121f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    }
122f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  }
123f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
124f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  /**
1250319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin   * Determines whether the sentinel is updated by checking the text of a
1260319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin   * descendant element of the sentinel (or itself).
127f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin   */
128f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  public static class TextUpdated extends SingleStringUpdated {
129f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    public TextUpdated(Finder uniqueStringFinder) {
130f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      super(uniqueStringFinder);
131f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    }
132f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
133f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    @Override
1340319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin    protected String getUniqueString(UiElement uniqueStringElement) {
1350319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin      return uniqueStringElement.getText();
136f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    }
137f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  }
138f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
139f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  /**
140f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin   * Determines whether the sentinel is updated by checking the content
1410319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin   * description of a descendant element of the sentinel (or itself).
142f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin   */
143f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  public static class ContentDescriptionUpdated extends SingleStringUpdated {
144f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    public ContentDescriptionUpdated(Finder uniqueStringFinder) {
145f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin      super(uniqueStringFinder);
146f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    }
147f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
148f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    @Override
1490319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin    protected String getUniqueString(UiElement uniqueStringElement) {
1500319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin      return uniqueStringElement.getContentDescription();
151f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    }
152f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  }
153f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
154dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin  /**
155dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin   * Determines whether the sentinel is updated by checking the resource-id of a
156dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin   * descendant element of the sentinel (often itself). This is useful when the
157dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin   * children of the container are heterogeneous -- they don't have a common
158dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin   * pattern to get a unique string.
159dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin   */
160dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin  public static class ResourceIdUpdated extends SingleStringUpdated {
161dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin    /**
162dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin     * Uses the resource-id of the sentinel itself.
163dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin     */
164dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin    public static final ResourceIdUpdated SELF = new ResourceIdUpdated(By.any());
165dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin
166dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin    public ResourceIdUpdated(Finder uniqueStringFinder) {
167dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin      super(uniqueStringFinder);
168dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin    }
169dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin
170dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin    @Override
171dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin    protected String getUniqueString(UiElement uniqueStringElement) {
172dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin      return uniqueStringElement.getResourceId();
173dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin    }
174dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin  }
175dfc316e1bfb37148c50947c46f5aaed5cb2e708aKevin Jin
176f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  private final IsUpdatedStrategy isUpdatedStrategy;
177026e2d0318ee13637adbc71365592705c149c157Kevin Jin  private UiElement lastSentinel;
178f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
179f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  /**
1809c92f46280cf3943701e75349833c68b584992e2Kevin Jin   * Constructs with {@code Getter}s that decorate the given {@code Getter}s
1819c92f46280cf3943701e75349833c68b584992e2Kevin Jin   * with {@link UiElement#VISIBLE}, and the given {@code isUpdatedStrategy} and
1829c92f46280cf3943701e75349833c68b584992e2Kevin Jin   * {@code directionConverter}. Be careful with {@code Getter}s: the sentinel
1839c92f46280cf3943701e75349833c68b584992e2Kevin Jin   * after each scroll should be unique.
184f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin   */
1859c92f46280cf3943701e75349833c68b584992e2Kevin Jin  public DynamicSentinelStrategy(IsUpdatedStrategy isUpdatedStrategy, Getter backwardGetter,
1869c92f46280cf3943701e75349833c68b584992e2Kevin Jin      Getter forwardGetter, DirectionConverter directionConverter) {
187e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin    super(new MorePredicateGetter(backwardGetter, UiElement.VISIBLE), new MorePredicateGetter(
188e0ad7adf66e62e536a0ce66fcb099a3518cda010Kevin Jin        forwardGetter, UiElement.VISIBLE), directionConverter);
189f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    this.isUpdatedStrategy = isUpdatedStrategy;
190f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  }
191f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
192f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  /**
193b5194043e9f0a1319dc7251f829febab3c76e277Kevin Jin   * Defaults to the standard {@link DirectionConverter}.
194f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin   */
1959c92f46280cf3943701e75349833c68b584992e2Kevin Jin  public DynamicSentinelStrategy(IsUpdatedStrategy isUpdatedStrategy, Getter backwardGetter,
1969c92f46280cf3943701e75349833c68b584992e2Kevin Jin      Getter forwardGetter) {
1979c92f46280cf3943701e75349833c68b584992e2Kevin Jin    this(isUpdatedStrategy, backwardGetter, forwardGetter, DirectionConverter.STANDARD_CONVERTER);
198f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  }
199f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
200f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  /**
201f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin   * Defaults to LAST_CHILD_GETTER for forward scrolling, and the standard
202b5194043e9f0a1319dc7251f829febab3c76e277Kevin Jin   * {@link DirectionConverter}.
203f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin   */
2049c92f46280cf3943701e75349833c68b584992e2Kevin Jin  public DynamicSentinelStrategy(IsUpdatedStrategy isUpdatedStrategy, Getter backwardGetter) {
2059c92f46280cf3943701e75349833c68b584992e2Kevin Jin    this(isUpdatedStrategy, backwardGetter, LAST_CHILD_GETTER,
206b5194043e9f0a1319dc7251f829febab3c76e277Kevin Jin        DirectionConverter.STANDARD_CONVERTER);
207f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  }
208f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
209f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  @Override
2100319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin  public boolean scroll(DroidDriver driver, Finder containerFinder, PhysicalDirection direction) {
211026e2d0318ee13637adbc71365592705c149c157Kevin Jin    UiElement oldSentinel = getOldSentinel(driver, containerFinder, direction);
2129031ed9b636ccd3b942eefb85dbfae2aed9e4f11Kevin Jin    doScroll(oldSentinel.getParent(), direction);
2130319e7c14a536a11851cc30cfa57241ce90fec11Kevin Jin    UiElement newSentinel = getSentinel(driver, containerFinder, direction);
214026e2d0318ee13637adbc71365592705c149c157Kevin Jin    lastSentinel = newSentinel;
215f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    return isUpdatedStrategy.isSentinelUpdated(newSentinel, oldSentinel);
216f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  }
217f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin
218026e2d0318ee13637adbc71365592705c149c157Kevin Jin  private UiElement getOldSentinel(DroidDriver driver, Finder containerFinder,
219026e2d0318ee13637adbc71365592705c149c157Kevin Jin      PhysicalDirection direction) {
220026e2d0318ee13637adbc71365592705c149c157Kevin Jin    return lastSentinel != null ? lastSentinel : getSentinel(driver, containerFinder, direction);
221026e2d0318ee13637adbc71365592705c149c157Kevin Jin  }
222026e2d0318ee13637adbc71365592705c149c157Kevin Jin
223026e2d0318ee13637adbc71365592705c149c157Kevin Jin  @Override
2245cf5f03c64b65b1f1ecd2140b8d6605ac05b6199Kevin Jin  public void beginScrolling(DroidDriver driver, Finder containerFinder, Finder itemFinder,
2255cf5f03c64b65b1f1ecd2140b8d6605ac05b6199Kevin Jin      PhysicalDirection direction) {
226026e2d0318ee13637adbc71365592705c149c157Kevin Jin    lastSentinel = null;
227026e2d0318ee13637adbc71365592705c149c157Kevin Jin  }
228026e2d0318ee13637adbc71365592705c149c157Kevin Jin
229026e2d0318ee13637adbc71365592705c149c157Kevin Jin  @Override
2305cf5f03c64b65b1f1ecd2140b8d6605ac05b6199Kevin Jin  public void endScrolling(DroidDriver driver, Finder containerFinder, Finder itemFinder,
2315cf5f03c64b65b1f1ecd2140b8d6605ac05b6199Kevin Jin      PhysicalDirection direction) {
232026e2d0318ee13637adbc71365592705c149c157Kevin Jin    // Prevent memory leak
233026e2d0318ee13637adbc71365592705c149c157Kevin Jin    lastSentinel = null;
234026e2d0318ee13637adbc71365592705c149c157Kevin Jin  }
235026e2d0318ee13637adbc71365592705c149c157Kevin Jin
236f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  @Override
237f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  public String toString() {
238f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin    return String.format("DynamicSentinelStrategy{%s, isUpdatedStrategy=%s}", super.toString(),
239f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin        isUpdatedStrategy);
240f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin  }
241f9c6c5063b38b623679e47d7095cccddb0481319Kevin Jin}
242