• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
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 package android.support.test.launcherhelper;
17 
18 import android.graphics.Rect;
19 import android.support.test.uiautomator.By;
20 import android.support.test.uiautomator.BySelector;
21 import android.support.test.uiautomator.Direction;
22 import android.support.test.uiautomator.UiDevice;
23 import android.support.test.uiautomator.UiObject2;
24 import android.support.test.uiautomator.UiObjectNotFoundException;
25 import android.support.test.uiautomator.Until;
26 import android.util.Log;
27 
28 /**
29  * A helper class for generic launcher interactions that can be abstracted across different types
30  * of launchers.
31  *
32  */
33 public class CommonLauncherHelper {
34 
35     private static final String LOG_TAG = CommonLauncherHelper.class.getSimpleName();
36     private static final int MAX_SCROLL_ATTEMPTS = 20;
37     private static final int MIN_INTERACT_SIZE = 100;
38     private static final int APP_LAUNCH_TIMEOUT = 10000;
39     private static CommonLauncherHelper sInstance;
40     private UiDevice mDevice;
41 
CommonLauncherHelper(UiDevice uiDevice)42     private CommonLauncherHelper(UiDevice uiDevice) {
43         mDevice = uiDevice;
44     }
45 
46     /**
47      * Retrieves the singleton instance of {@link CommonLauncherHelper}
48      * @param uiDevice
49      * @return
50      */
getInstance(UiDevice uiDevice)51     public static CommonLauncherHelper getInstance(UiDevice uiDevice) {
52         if (sInstance == null) {
53             sInstance = new CommonLauncherHelper(uiDevice);
54         }
55         return sInstance;
56     }
57 
58     /**
59      * Scrolls a container back to the beginning
60      * @param container
61      * @param backDirection
62      * @throws UiObjectNotFoundException
63      */
scrollBackToBeginning(UiObject2 container, Direction backDirection)64     public void scrollBackToBeginning(UiObject2 container, Direction backDirection)
65             throws UiObjectNotFoundException {
66         scrollBackToBeginning(container, backDirection, MAX_SCROLL_ATTEMPTS);
67     }
68 
69     /**
70      * Scrolls a container back to the beginning
71      * @param container
72      * @param backDirection
73      * @param maxAttempts
74      * @throws UiObjectNotFoundException
75      */
scrollBackToBeginning(UiObject2 container, Direction backDirection, int maxAttempts)76     public void scrollBackToBeginning(UiObject2 container, Direction backDirection, int maxAttempts)
77             throws UiObjectNotFoundException {
78         int attempts = 0;
79         while (container.fling(backDirection)) {
80             attempts++;
81             if (attempts > maxAttempts) {
82                 throw new RuntimeException(
83                         "scrollBackToBeginning: exceeded max attampts: " + maxAttempts);
84             }
85         }
86     }
87 
88     /**
89      * Ensures that the described widget has enough visible portion by scrolling its container if
90      * necessary
91      * @param app
92      * @param container
93      * @param dir
94      * @throws UiObjectNotFoundException
95      */
ensureIconVisible(BySelector app, UiObject2 container, Direction dir)96     private void ensureIconVisible(BySelector app, UiObject2 container, Direction dir)
97             throws UiObjectNotFoundException {
98         UiObject2 appIcon = mDevice.findObject(app);
99         Rect appR = appIcon.getVisibleBounds();
100         Rect containerR = container.getVisibleBounds();
101         int size = 0;
102         int containerSize = 0;
103         if (Direction.DOWN.equals(dir) || Direction.UP.equals(dir)) {
104             size = appR.height();
105             containerSize = containerR.height();
106         } else {
107             size = appR.width();
108             containerSize = containerR.width();
109         }
110         if (size < MIN_INTERACT_SIZE) {
111             // try to figure out how much percentage of the container needs to be scrolled in order
112             // to reveal the app icon to have the MIN_INTERACT_SIZE
113             float pct = ((float)(MIN_INTERACT_SIZE - size)) / containerSize;
114             if (pct < 0.2f) {
115                 pct = 0.2f;
116             }
117             container.scroll(dir, pct);
118         }
119     }
120 
121     /**
122      * Triggers app launch by interacting with its launcher icon as described, optionally verify
123      * that the frontend UI has the expected app package name
124      * @param launcherStrategy
125      * @param app
126      * @param packageName
127      * @return
128      * @throws UiObjectNotFoundException
129      */
launchApp(ILauncherStrategy launcherStrategy, BySelector app, String packageName)130     public boolean launchApp(ILauncherStrategy launcherStrategy, BySelector app,
131             String packageName) throws UiObjectNotFoundException {
132         return launchApp(launcherStrategy, app, packageName, MAX_SCROLL_ATTEMPTS);
133     }
134 
135     /**
136      * Triggers app launch by interacting with its launcher icon as described, optionally verify
137      * that the frontend UI has the expected app package name
138      * @param launcherStrategy
139      * @param app
140      * @param packageName
141      * @param maxScrollAttempts
142      * @return
143      * @throws UiObjectNotFoundException
144      */
launchApp(ILauncherStrategy launcherStrategy, BySelector app, String packageName, int maxScrollAttempts)145     public boolean launchApp(ILauncherStrategy launcherStrategy, BySelector app,
146             String packageName, int maxScrollAttempts)
147                     throws UiObjectNotFoundException {
148         Direction dir = launcherStrategy.getAllAppsScrollDirection();
149         // attempt to find the app icon if it's not already on the screen
150         if (!mDevice.hasObject(app)) {
151             UiObject2 container = launcherStrategy.openAllApps(false);
152 
153             if (!mDevice.hasObject(app)) {
154                 scrollBackToBeginning(container, Direction.reverse(dir));
155                 int attempts = 0;
156                 while (!mDevice.hasObject(app) && container.scroll(dir, 0.8f)) {
157                     attempts++;
158                     if (attempts > maxScrollAttempts) {
159                         throw new RuntimeException(
160                                 "launchApp: exceeded max attampts to locate app icon: "
161                                         + maxScrollAttempts);
162                     }
163                 }
164             }
165             // HACK-ish: ensure icon has enough parts revealed for it to be clicked on
166             ensureIconVisible(app, container, dir);
167         }
168 
169         if (!mDevice.findObject(app).clickAndWait(Until.newWindow(), APP_LAUNCH_TIMEOUT)) {
170             Log.w(LOG_TAG, "no new window detected after app launch attempt.");
171             return false;
172         }
173         mDevice.waitForIdle();
174         if (packageName != null) {
175             Log.w(LOG_TAG, String.format(
176                     "No UI element with package name %s detected.", packageName));
177             return mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), APP_LAUNCH_TIMEOUT);
178         } else {
179             return true;
180         }
181     }
182 }
183