• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.android.launcher3.ui;
17 
18 import android.app.Instrumentation;
19 import android.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.pm.LauncherActivityInfo;
25 import android.graphics.Point;
26 import android.os.Process;
27 import android.os.RemoteException;
28 import android.os.SystemClock;
29 import android.support.test.InstrumentationRegistry;
30 import android.support.test.uiautomator.By;
31 import android.support.test.uiautomator.BySelector;
32 import android.support.test.uiautomator.Direction;
33 import android.support.test.uiautomator.UiDevice;
34 import android.support.test.uiautomator.UiObject2;
35 import android.support.test.uiautomator.Until;
36 import android.view.MotionEvent;
37 
38 import com.android.launcher3.LauncherAppState;
39 import com.android.launcher3.LauncherAppWidgetProviderInfo;
40 import com.android.launcher3.LauncherSettings;
41 import com.android.launcher3.MainThreadExecutor;
42 import com.android.launcher3.R;
43 import com.android.launcher3.compat.AppWidgetManagerCompat;
44 import com.android.launcher3.compat.LauncherAppsCompat;
45 import com.android.launcher3.config.FeatureFlags;
46 import com.android.launcher3.testcomponent.AppWidgetNoConfig;
47 import com.android.launcher3.testcomponent.AppWidgetWithConfig;
48 
49 import org.junit.Before;
50 
51 import java.util.Locale;
52 import java.util.concurrent.Callable;
53 import java.util.concurrent.CountDownLatch;
54 import java.util.concurrent.TimeUnit;
55 
56 import static org.junit.Assert.assertNotNull;
57 import static org.junit.Assert.assertNull;
58 
59 /**
60  * Base class for all instrumentation tests providing various utility methods.
61  */
62 public abstract class AbstractLauncherUiTest {
63 
64     public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
65     public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
66 
67     public static final long DEFAULT_UI_TIMEOUT = 3000;
68     public static final long LARGE_UI_TIMEOUT = 10000;
69     public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5;
70 
71     protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
72     protected UiDevice mDevice;
73     protected Context mTargetContext;
74     protected String mTargetPackage;
75 
76     @Before
setUp()77     public void setUp() throws Exception {
78         mDevice = UiDevice.getInstance(getInstrumentation());
79         mTargetContext = InstrumentationRegistry.getTargetContext();
80         mTargetPackage = mTargetContext.getPackageName();
81     }
82 
lockRotation(boolean naturalOrientation)83     protected void lockRotation(boolean naturalOrientation) throws RemoteException {
84         if (naturalOrientation) {
85             mDevice.setOrientationNatural();
86         } else {
87             mDevice.setOrientationRight();
88         }
89     }
90 
getInstrumentation()91     protected Instrumentation getInstrumentation() {
92         return InstrumentationRegistry.getInstrumentation();
93     }
94 
95     /**
96      * Opens all apps and returns the recycler view
97      */
openAllApps()98     protected UiObject2 openAllApps() {
99         mDevice.waitForIdle();
100         if (FeatureFlags.NO_ALL_APPS_ICON) {
101             UiObject2 hotseat = mDevice.wait(
102                     Until.findObject(getSelectorForId(R.id.hotseat)), 2500);
103             Point start = hotseat.getVisibleCenter();
104             int endY = (int) (mDevice.getDisplayHeight() * 0.1f);
105             // 100 px/step
106             mDevice.swipe(start.x, start.y, start.x, endY, (start.y - endY) / 100);
107 
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, boolean expectedToShowShortcuts)144     protected void dragToWorkspace(UiObject2 icon, boolean expectedToShowShortcuts) {
145         Point center = icon.getVisibleCenter();
146 
147         // Action Down
148         sendPointer(MotionEvent.ACTION_DOWN, center);
149 
150         UiObject2 dragLayer = findViewById(R.id.drag_layer);
151 
152         if (expectedToShowShortcuts) {
153             // Make sure shortcuts show up, and then move a bit to hide them.
154             assertNotNull(findViewById(R.id.deep_shortcuts_container));
155 
156             Point moveLocation = new Point(center);
157             int distanceToMove = mTargetContext.getResources().getDimensionPixelSize(
158                     R.dimen.deep_shortcuts_start_drag_threshold) + 50;
159             if (moveLocation.y - distanceToMove >= dragLayer.getVisibleBounds().top) {
160                 moveLocation.y -= distanceToMove;
161             } else {
162                 moveLocation.y += distanceToMove;
163             }
164             movePointer(center, moveLocation);
165 
166             assertNull(findViewById(R.id.deep_shortcuts_container));
167         }
168 
169         // Wait until Remove/Delete target is visible
170         assertNotNull(findViewById(R.id.delete_target_text));
171 
172         Point moveLocation = dragLayer.getVisibleCenter();
173 
174         // Move to center
175         movePointer(center, moveLocation);
176         sendPointer(MotionEvent.ACTION_UP, center);
177 
178         // Wait until remove target is gone.
179         mDevice.wait(Until.gone(getSelectorForId(R.id.delete_target_text)), DEFAULT_UI_TIMEOUT);
180     }
181 
movePointer(Point from, Point to)182     private void movePointer(Point from, Point to) {
183         while(!from.equals(to)) {
184             from.x = getNextMoveValue(to.x, from.x);
185             from.y = getNextMoveValue(to.y, from.y);
186             sendPointer(MotionEvent.ACTION_MOVE, from);
187         }
188     }
189 
getNextMoveValue(int targetValue, int oldValue)190     private int getNextMoveValue(int targetValue, int oldValue) {
191         if (targetValue - oldValue > 10) {
192             return oldValue + 10;
193         } else if (targetValue - oldValue < -10) {
194             return oldValue - 10;
195         } else {
196             return targetValue;
197         }
198     }
199 
sendPointer(int action, Point point)200     protected void sendPointer(int action, Point point) {
201         MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
202                 SystemClock.uptimeMillis(), action, point.x, point.y, 0);
203         getInstrumentation().sendPointerSync(event);
204         event.recycle();
205     }
206 
207     /**
208      * Removes all icons from homescreen and hotseat.
209      */
clearHomescreen()210     public void clearHomescreen() throws Throwable {
211         LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
212                 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
213         LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
214                 LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
215         resetLoaderState();
216     }
217 
resetLoaderState()218     protected void resetLoaderState() {
219         try {
220             mMainThreadExecutor.execute(new Runnable() {
221                 @Override
222                 public void run() {
223                     LauncherAppState.getInstance(mTargetContext).getModel().forceReload();
224                 }
225             });
226         } catch (Throwable t) {
227             throw new IllegalArgumentException(t);
228         }
229     }
230 
231     /**
232      * Runs the callback on the UI thread and returns the result.
233      */
getOnUiThread(final Callable<T> callback)234     protected <T> T getOnUiThread(final Callable<T> callback) {
235         try {
236             return mMainThreadExecutor.submit(callback).get();
237         } catch (Exception e) {
238             throw new RuntimeException(e);
239         }
240     }
241 
242     /**
243      * Finds a widget provider which can fit on the home screen.
244      * @param hasConfigureScreen if true, a provider with a config screen is returned.
245      */
findWidgetProvider(final boolean hasConfigureScreen)246     protected LauncherAppWidgetProviderInfo findWidgetProvider(final boolean hasConfigureScreen) {
247         LauncherAppWidgetProviderInfo info =
248                 getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
249             @Override
250             public LauncherAppWidgetProviderInfo call() throws Exception {
251                 ComponentName cn = new ComponentName(getInstrumentation().getContext(),
252                         hasConfigureScreen ? AppWidgetWithConfig.class : AppWidgetNoConfig.class);
253                 return AppWidgetManagerCompat.getInstance(mTargetContext)
254                         .findProvider(cn, Process.myUserHandle());
255             }
256         });
257         if (info == null) {
258             throw new IllegalArgumentException("No valid widget provider");
259         }
260         return info;
261     }
262 
findViewById(int id)263     protected UiObject2 findViewById(int id) {
264         return mDevice.wait(Until.findObject(getSelectorForId(id)), DEFAULT_UI_TIMEOUT);
265     }
266 
getSelectorForId(int id)267     protected BySelector getSelectorForId(int id) {
268         String name = mTargetContext.getResources().getResourceEntryName(id);
269         return By.res(mTargetPackage, name);
270     }
271 
getSettingsApp()272     protected LauncherActivityInfo getSettingsApp() {
273         return LauncherAppsCompat.getInstance(mTargetContext)
274                 .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
275     }
276 
277     /**
278      * Broadcast receiver which blocks until the result is received.
279      */
280     public class BlockingBroadcastReceiver extends BroadcastReceiver {
281 
282         private final CountDownLatch latch = new CountDownLatch(1);
283         private Intent mIntent;
284 
BlockingBroadcastReceiver(String action)285         public BlockingBroadcastReceiver(String action) {
286             mTargetContext.registerReceiver(this, new IntentFilter(action));
287         }
288 
289         @Override
onReceive(Context context, Intent intent)290         public void onReceive(Context context, Intent intent) {
291             mIntent = intent;
292             latch.countDown();
293         }
294 
blockingGetIntent()295         public Intent blockingGetIntent() throws InterruptedException {
296             latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
297             mTargetContext.unregisterReceiver(this);
298             return mIntent;
299         }
300 
blockingGetExtraIntent()301         public Intent blockingGetExtraIntent() throws InterruptedException {
302             Intent intent = blockingGetIntent();
303             return intent == null ? null : (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT);
304         }
305     }
306 }
307