• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.launcher3.ui;
2 
3 import android.app.SearchManager;
4 import android.appwidget.AppWidgetProviderInfo;
5 import android.content.ComponentName;
6 import android.content.Context;
7 import android.content.Intent;
8 import android.content.pm.PackageManager;
9 import android.graphics.Point;
10 import android.os.ParcelFileDescriptor;
11 import android.os.RemoteException;
12 import android.os.SystemClock;
13 import android.support.test.uiautomator.By;
14 import android.support.test.uiautomator.BySelector;
15 import android.support.test.uiautomator.Direction;
16 import android.support.test.uiautomator.UiDevice;
17 import android.support.test.uiautomator.UiObject2;
18 import android.support.test.uiautomator.Until;
19 import android.test.InstrumentationTestCase;
20 import android.view.MotionEvent;
21 
22 import com.android.launcher3.InvariantDeviceProfile;
23 import com.android.launcher3.Launcher;
24 import com.android.launcher3.LauncherAppState;
25 import com.android.launcher3.LauncherAppWidgetProviderInfo;
26 import com.android.launcher3.LauncherClings;
27 import com.android.launcher3.LauncherSettings;
28 import com.android.launcher3.R;
29 import com.android.launcher3.Utilities;
30 import com.android.launcher3.compat.AppWidgetManagerCompat;
31 import com.android.launcher3.config.FeatureFlags;
32 import com.android.launcher3.util.ManagedProfileHeuristic;
33 
34 import java.io.FileInputStream;
35 import java.io.IOException;
36 import java.util.Locale;
37 import java.util.concurrent.Callable;
38 import java.util.concurrent.atomic.AtomicReference;
39 
40 /**
41  * Base class for all instrumentation tests providing various utility methods.
42  */
43 public class LauncherInstrumentationTestCase extends InstrumentationTestCase {
44 
45     public static final long DEFAULT_UI_TIMEOUT = 3000;
46 
47     protected UiDevice mDevice;
48     protected Context mTargetContext;
49     protected String mTargetPackage;
50 
51     @Override
setUp()52     protected void setUp() throws Exception {
53         super.setUp();
54 
55         mDevice = UiDevice.getInstance(getInstrumentation());
56         mTargetContext = getInstrumentation().getTargetContext();
57         mTargetPackage = mTargetContext.getPackageName();
58     }
59 
lockRotation(boolean naturalOrientation)60     protected void lockRotation(boolean naturalOrientation) throws RemoteException {
61         Utilities.getPrefs(mTargetContext)
62                 .edit()
63                 .putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, !naturalOrientation)
64                 .commit();
65 
66         if (naturalOrientation) {
67             mDevice.setOrientationNatural();
68         } else {
69             mDevice.setOrientationRight();
70         }
71     }
72 
73     /**
74      * Starts the launcher activity in the target package and returns the Launcher instance.
75      */
startLauncher()76     protected Launcher startLauncher() {
77         Intent homeIntent = new Intent(Intent.ACTION_MAIN)
78                 .addCategory(Intent.CATEGORY_HOME)
79                 .setPackage(mTargetPackage)
80                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
81         return (Launcher) getInstrumentation().startActivitySync(homeIntent);
82     }
83 
84     /**
85      * Grants the launcher permission to bind widgets.
86      */
grantWidgetPermission()87     protected void grantWidgetPermission() throws IOException {
88         // Check bind widget permission
89         if (mTargetContext.getPackageManager().checkPermission(
90                 mTargetPackage, android.Manifest.permission.BIND_APPWIDGET)
91                 != PackageManager.PERMISSION_GRANTED) {
92             ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(
93                     "appwidget grantbind --package " + mTargetPackage);
94             // Read the input stream fully.
95             FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
96             while (fis.read() != -1);
97             fis.close();
98         }
99     }
100 
101     /**
102      * Opens all apps and returns the recycler view
103      */
openAllApps()104     protected UiObject2 openAllApps() {
105         if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
106             // clicking on the page indicator brings up all apps tray on non tablets.
107             findViewById(R.id.page_indicator).click();
108         } else {
109             mDevice.wait(Until.findObject(
110                     By.desc(mTargetContext.getString(R.string.all_apps_button_label))),
111                     DEFAULT_UI_TIMEOUT).click();
112         }
113         return findViewById(R.id.apps_list_view);
114     }
115 
116     /**
117      * Opens widget tray and returns the recycler view.
118      */
openWidgetsTray()119     protected UiObject2 openWidgetsTray() {
120         mDevice.pressMenu(); // Enter overview mode.
121         mDevice.wait(Until.findObject(
122                 By.text(mTargetContext.getString(R.string.widget_button_text)
123                         .toUpperCase(Locale.getDefault()))), DEFAULT_UI_TIMEOUT).click();
124         return findViewById(R.id.widgets_list_view);
125     }
126 
127     /**
128      * Scrolls the {@param container} until it finds an object matching {@param condition}.
129      * @return the matching object.
130      */
scrollAndFind(UiObject2 container, BySelector condition)131     protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) {
132         do {
133             UiObject2 widget = container.findObject(condition);
134             if (widget != null) {
135                 return widget;
136             }
137         } while (container.scroll(Direction.DOWN, 1f));
138         return container.findObject(condition);
139     }
140 
141     /**
142      * Drags an icon to the center of homescreen.
143      */
dragToWorkspace(UiObject2 icon)144     protected void dragToWorkspace(UiObject2 icon) {
145         Point center = icon.getVisibleCenter();
146 
147         // Action Down
148         sendPointer(MotionEvent.ACTION_DOWN, center);
149 
150         // Wait until "Remove/Delete target is visible
151         assertNotNull(findViewById(R.id.delete_target_text));
152 
153         Point moveLocation = findViewById(R.id.drag_layer).getVisibleCenter();
154 
155         // Move to center
156         while(!moveLocation.equals(center)) {
157             center.x = getNextMoveValue(moveLocation.x, center.x);
158             center.y = getNextMoveValue(moveLocation.y, center.y);
159             sendPointer(MotionEvent.ACTION_MOVE, center);
160         }
161         sendPointer(MotionEvent.ACTION_UP, center);
162 
163         // Wait until remove target is gone.
164         mDevice.wait(Until.gone(getSelectorForId(R.id.delete_target_text)), DEFAULT_UI_TIMEOUT);
165     }
166 
getNextMoveValue(int targetValue, int oldValue)167     private int getNextMoveValue(int targetValue, int oldValue) {
168         if (targetValue - oldValue > 10) {
169             return oldValue + 10;
170         } else if (targetValue - oldValue < -10) {
171             return oldValue - 10;
172         } else {
173             return targetValue;
174         }
175     }
176 
sendPointer(int action, Point point)177     private void sendPointer(int action, Point point) {
178         MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
179                 SystemClock.uptimeMillis(), action, point.x, point.y, 0);
180         getInstrumentation().sendPointerSync(event);
181         event.recycle();
182     }
183 
184     /**
185      * Removes all icons from homescreen and hotseat.
186      */
clearHomescreen()187     public void clearHomescreen() throws Throwable {
188         LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
189                 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
190         LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
191                 LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
192         LauncherClings.markFirstRunClingDismissed(mTargetContext);
193         ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
194 
195         runTestOnUiThread(new Runnable() {
196             @Override
197             public void run() {
198                 // Reset the loader state
199                 LauncherAppState.getInstance().getModel().resetLoadedState(true, true);
200             }
201         });
202     }
203 
204     /**
205      * Runs the callback on the UI thread and returns the result.
206      */
getOnUiThread(final Callable<T> callback)207     protected <T> T getOnUiThread(final Callable<T> callback) {
208         final AtomicReference<T> result = new AtomicReference<>(null);
209         try {
210             runTestOnUiThread(new Runnable() {
211                 @Override
212                 public void run() {
213                     try {
214                         result.set(callback.call());
215                     } catch (Exception e) { }
216                 }
217             });
218         } catch (Throwable t) { }
219         return result.get();
220     }
221 
222     /**
223      * Finds a widget provider which can fit on the home screen.
224      * @param hasConfigureScreen if true, a provider with a config screen is returned.
225      */
findWidgetProvider(final boolean hasConfigureScreen)226     protected LauncherAppWidgetProviderInfo findWidgetProvider(final boolean hasConfigureScreen) {
227         LauncherAppWidgetProviderInfo info = getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
228             @Override
229             public LauncherAppWidgetProviderInfo call() throws Exception {
230                 InvariantDeviceProfile idv =
231                         LauncherAppState.getInstance().getInvariantDeviceProfile();
232 
233                 ComponentName searchComponent = ((SearchManager) mTargetContext
234                         .getSystemService(Context.SEARCH_SERVICE)).getGlobalSearchActivity();
235                 String searchPackage = searchComponent == null
236                         ? null : searchComponent.getPackageName();
237 
238                 for (AppWidgetProviderInfo info :
239                         AppWidgetManagerCompat.getInstance(mTargetContext).getAllProviders()) {
240                     if ((info.configure != null) ^ hasConfigureScreen) {
241                         continue;
242                     }
243                     // Exclude the widgets in search package, as Launcher already binds them in
244                     // QSB, so they can cause conflicts.
245                     if (info.provider.getPackageName().equals(searchPackage)) {
246                         continue;
247                     }
248                     LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
249                             .fromProviderInfo(mTargetContext, info);
250                     if (widgetInfo.minSpanX >= idv.numColumns
251                             || widgetInfo.minSpanY >= idv.numRows) {
252                         continue;
253                     }
254                     return widgetInfo;
255                 }
256                 return null;
257             }
258         });
259         if (info == null) {
260             throw new IllegalArgumentException("No valid widget provider");
261         }
262         return info;
263     }
264 
findViewById(int id)265     protected UiObject2 findViewById(int id) {
266         return mDevice.wait(Until.findObject(getSelectorForId(id)), DEFAULT_UI_TIMEOUT);
267     }
268 
getSelectorForId(int id)269     protected BySelector getSelectorForId(int id) {
270         String name = mTargetContext.getResources().getResourceEntryName(id);
271         return By.res(mTargetPackage, name);
272     }
273 }
274