1 /*
2  * Copyright (C) 2013 DroidDriver committers
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 
17 package io.appium.droiddriver.scroll;
18 
19 import android.app.UiAutomation;
20 import android.widget.ProgressBar;
21 
22 import io.appium.droiddriver.DroidDriver;
23 import io.appium.droiddriver.finders.By;
24 import io.appium.droiddriver.finders.Finder;
25 import io.appium.droiddriver.scroll.Direction.Axis;
26 import io.appium.droiddriver.scroll.Direction.DirectionConverter;
27 import io.appium.droiddriver.scroll.Direction.PhysicalDirection;
28 
29 /**
30  * Static utility classes and methods pertaining to {@link Scroller} instances.
31  */
32 public class Scrollers {
33   /**
34    * Augments the delegate {@link ScrollStepStrategy} - after a successful
35    * scroll, waits until ProgressBar is gone.
36    */
37   public static abstract class ProgressBarScrollStepStrategy extends ForwardingScrollStepStrategy {
38     @Override
scroll(DroidDriver driver, Finder containerFinder, PhysicalDirection direction)39     public boolean scroll(DroidDriver driver, Finder containerFinder, PhysicalDirection direction) {
40       if (super.scroll(driver, containerFinder, direction)) {
41         driver.checkGone(By.className(ProgressBar.class));
42         return true;
43       }
44       return false;
45     }
46 
47     /** Convenience method to wrap {@code delegate} with this class */
wrap(final ScrollStepStrategy delegate)48     public static ScrollStepStrategy wrap(final ScrollStepStrategy delegate) {
49       return new ProgressBarScrollStepStrategy() {
50         @Override
51         protected ScrollStepStrategy delegate() {
52           return delegate;
53         }
54       };
55     }
56   }
57 
58   /**
59    * Returns a new default Scroller that works in simple cases. In complex cases
60    * you may try a {@link StepBasedScroller} with a custom
61    * {@link ScrollStepStrategy}:
62    * <ul>
63    * <li>If the Scroller is used with InstrumentationDriver,
64    * StaticSentinelStrategy may work and it's the simplest.</li>
65    * <li>Otherwise, DynamicSentinelStrategy should work in all cases, including
66    * the case of dynamic list, which shows more items when scrolling beyond the
67    * end. On the other hand, it's complex and needs more configuration.</li>
68    * </ul>
69    * Note if a {@link StepBasedScroller} is returned, it is constructed with
70    * arguments that apply to typical cases. You may want to customize them for
71    * specific cases. For instance, {@code perScrollTimeoutMillis} can be 0L if
72    * there are no asynchronously updated views. To that extent, this method
73    * serves as an example of how to construct {@link Scroller}s rather than
74    * providing the "official" {@link Scroller}.
75    */
76   public static Scroller newScroller(UiAutomation uiAutomation) {
77     if (uiAutomation != null) {
78       return new StepBasedScroller(100/* maxScrolls */, 1000L/* perScrollTimeoutMillis */,
79           Axis.VERTICAL, new AccessibilityEventScrollStepStrategy(uiAutomation, 1000L,
80               DirectionConverter.STANDARD_CONVERTER), true/* startFromBeginning */);
81     }
82     // TODO: A {@link Scroller} that directly jumps to the view if an
83     // InstrumentationDriver is used.
84     return new StepBasedScroller(100/* maxScrolls */, 1000L/* perScrollTimeoutMillis */,
85         Axis.VERTICAL, StaticSentinelStrategy.DEFAULT, true/* startFromBeginning */);
86   }
87 }
88