• 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.Utilities;
44 import com.android.launcher3.compat.AppWidgetManagerCompat;
45 import com.android.launcher3.compat.LauncherAppsCompat;
46 import com.android.launcher3.config.FeatureFlags;
47 import com.android.launcher3.testcomponent.AppWidgetNoConfig;
48 import com.android.launcher3.testcomponent.AppWidgetWithConfig;
49 import com.android.launcher3.util.ManagedProfileHeuristic;
50 
51 import org.junit.Before;
52 
53 import java.util.Locale;
54 import java.util.concurrent.Callable;
55 import java.util.concurrent.CountDownLatch;
56 import java.util.concurrent.TimeUnit;
57 
58 import static org.junit.Assert.assertNotNull;
59 import static org.junit.Assert.assertNull;
60 
61 /**
62  * Base class for all instrumentation tests providing various utility methods.
63  */
64 public abstract class AbstractLauncherUiTest {
65 
66     public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
67     public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
68 
69     public static final long DEFAULT_UI_TIMEOUT = 3000;
70     public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5;
71 
72     protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
73     protected UiDevice mDevice;
74     protected Context mTargetContext;
75     protected String mTargetPackage;
76 
77     @Before
setUp()78     public void setUp() throws Exception {
79         mDevice = UiDevice.getInstance(getInstrumentation());
80         mTargetContext = InstrumentationRegistry.getTargetContext();
81         mTargetPackage = mTargetContext.getPackageName();
82     }
83 
lockRotation(boolean naturalOrientation)84     protected void lockRotation(boolean naturalOrientation) throws RemoteException {
85         Utilities.getPrefs(mTargetContext)
86                 .edit()
87                 .putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, !naturalOrientation)
88                 .commit();
89 
90         if (naturalOrientation) {
91             mDevice.setOrientationNatural();
92         } else {
93             mDevice.setOrientationRight();
94         }
95     }
96 
getInstrumentation()97     protected Instrumentation getInstrumentation() {
98         return InstrumentationRegistry.getInstrumentation();
99     }
100 
101     /**
102      * Opens all apps and returns the recycler view
103      */
openAllApps()104     protected UiObject2 openAllApps() {
105         mDevice.waitForIdle();
106         if (FeatureFlags.NO_ALL_APPS_ICON) {
107             // clicking on the page indicator brings up all apps tray on non tablets.
108             findViewById(R.id.page_indicator).click();
109         } else {
110             mDevice.wait(Until.findObject(
111                     By.desc(mTargetContext.getString(R.string.all_apps_button_label))),
112                     DEFAULT_UI_TIMEOUT).click();
113         }
114         return findViewById(R.id.apps_list_view);
115     }
116 
117     /**
118      * Opens widget tray and returns the recycler view.
119      */
openWidgetsTray()120     protected UiObject2 openWidgetsTray() {
121         mDevice.pressMenu(); // Enter overview mode.
122         mDevice.wait(Until.findObject(
123                 By.text(mTargetContext.getString(R.string.widget_button_text)
124                         .toUpperCase(Locale.getDefault()))), DEFAULT_UI_TIMEOUT).click();
125         return findViewById(R.id.widgets_list_view);
126     }
127 
128     /**
129      * Scrolls the {@param container} until it finds an object matching {@param condition}.
130      * @return the matching object.
131      */
scrollAndFind(UiObject2 container, BySelector condition)132     protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) {
133         do {
134             UiObject2 widget = container.findObject(condition);
135             if (widget != null) {
136                 return widget;
137             }
138         } while (container.scroll(Direction.DOWN, 1f));
139         return container.findObject(condition);
140     }
141 
142     /**
143      * Drags an icon to the center of homescreen.
144      */
dragToWorkspace(UiObject2 icon, boolean expectedToShowShortcuts)145     protected void dragToWorkspace(UiObject2 icon, boolean expectedToShowShortcuts) {
146         Point center = icon.getVisibleCenter();
147 
148         // Action Down
149         sendPointer(MotionEvent.ACTION_DOWN, center);
150 
151         UiObject2 dragLayer = findViewById(R.id.drag_layer);
152 
153         if (expectedToShowShortcuts) {
154             // Make sure shortcuts show up, and then move a bit to hide them.
155             assertNotNull(findViewById(R.id.deep_shortcuts_container));
156 
157             Point moveLocation = new Point(center);
158             int distanceToMove = mTargetContext.getResources().getDimensionPixelSize(
159                     R.dimen.deep_shortcuts_start_drag_threshold) + 50;
160             if (moveLocation.y - distanceToMove >= dragLayer.getVisibleBounds().top) {
161                 moveLocation.y -= distanceToMove;
162             } else {
163                 moveLocation.y += distanceToMove;
164             }
165             movePointer(center, moveLocation);
166 
167             assertNull(findViewById(R.id.deep_shortcuts_container));
168         }
169 
170         // Wait until Remove/Delete target is visible
171         assertNotNull(findViewById(R.id.delete_target_text));
172 
173         Point moveLocation = dragLayer.getVisibleCenter();
174 
175         // Move to center
176         movePointer(center, moveLocation);
177         sendPointer(MotionEvent.ACTION_UP, center);
178 
179         // Wait until remove target is gone.
180         mDevice.wait(Until.gone(getSelectorForId(R.id.delete_target_text)), DEFAULT_UI_TIMEOUT);
181     }
182 
movePointer(Point from, Point to)183     private void movePointer(Point from, Point to) {
184         while(!from.equals(to)) {
185             from.x = getNextMoveValue(to.x, from.x);
186             from.y = getNextMoveValue(to.y, from.y);
187             sendPointer(MotionEvent.ACTION_MOVE, from);
188         }
189     }
190 
getNextMoveValue(int targetValue, int oldValue)191     private int getNextMoveValue(int targetValue, int oldValue) {
192         if (targetValue - oldValue > 10) {
193             return oldValue + 10;
194         } else if (targetValue - oldValue < -10) {
195             return oldValue - 10;
196         } else {
197             return targetValue;
198         }
199     }
200 
sendPointer(int action, Point point)201     protected void sendPointer(int action, Point point) {
202         MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
203                 SystemClock.uptimeMillis(), action, point.x, point.y, 0);
204         getInstrumentation().sendPointerSync(event);
205         event.recycle();
206     }
207 
208     /**
209      * Removes all icons from homescreen and hotseat.
210      */
clearHomescreen()211     public void clearHomescreen() throws Throwable {
212         LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
213                 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
214         LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
215                 LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
216         resetLoaderState();
217     }
218 
resetLoaderState()219     protected void resetLoaderState() {
220         try {
221             mMainThreadExecutor.execute(new Runnable() {
222                 @Override
223                 public void run() {
224                     ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
225                     LauncherAppState.getInstance(mTargetContext).getModel().forceReload();
226                 }
227             });
228         } catch (Throwable t) {
229             throw new IllegalArgumentException(t);
230         }
231     }
232 
233     /**
234      * Runs the callback on the UI thread and returns the result.
235      */
getOnUiThread(final Callable<T> callback)236     protected <T> T getOnUiThread(final Callable<T> callback) {
237         try {
238             return mMainThreadExecutor.submit(callback).get();
239         } catch (Exception e) {
240             throw new RuntimeException(e);
241         }
242     }
243 
244     /**
245      * Finds a widget provider which can fit on the home screen.
246      * @param hasConfigureScreen if true, a provider with a config screen is returned.
247      */
findWidgetProvider(final boolean hasConfigureScreen)248     protected LauncherAppWidgetProviderInfo findWidgetProvider(final boolean hasConfigureScreen) {
249         LauncherAppWidgetProviderInfo info =
250                 getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
251             @Override
252             public LauncherAppWidgetProviderInfo call() throws Exception {
253                 ComponentName cn = new ComponentName(getInstrumentation().getContext(),
254                         hasConfigureScreen ? AppWidgetWithConfig.class : AppWidgetNoConfig.class);
255                 return AppWidgetManagerCompat.getInstance(mTargetContext)
256                         .findProvider(cn, Process.myUserHandle());
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 
getSettingsApp()274     protected LauncherActivityInfo getSettingsApp() {
275         return LauncherAppsCompat.getInstance(mTargetContext)
276                 .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
277     }
278 
279     /**
280      * Broadcast receiver which blocks until the result is received.
281      */
282     public class BlockingBroadcastReceiver extends BroadcastReceiver {
283 
284         private final CountDownLatch latch = new CountDownLatch(1);
285         private Intent mIntent;
286 
BlockingBroadcastReceiver(String action)287         public BlockingBroadcastReceiver(String action) {
288             mTargetContext.registerReceiver(this, new IntentFilter(action));
289         }
290 
291         @Override
onReceive(Context context, Intent intent)292         public void onReceive(Context context, Intent intent) {
293             mIntent = intent;
294             latch.countDown();
295         }
296 
blockingGetIntent()297         public Intent blockingGetIntent() throws InterruptedException {
298             latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
299             mTargetContext.unregisterReceiver(this);
300             return mIntent;
301         }
302 
blockingGetExtraIntent()303         public Intent blockingGetExtraIntent() throws InterruptedException {
304             Intent intent = blockingGetIntent();
305             return intent == null ? null : (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT);
306         }
307     }
308 }
309