Merge "Adding some UI tests > Launcher app from all-apps > Drag icon to all-apps and launch it > Add widget from widget tray" into ub-launcher3-calgary
This commit is contained in:
@@ -72,7 +72,7 @@ public class LauncherAppWidgetInfo extends ItemInfo {
|
||||
*/
|
||||
int appWidgetId = NO_ID;
|
||||
|
||||
ComponentName providerName;
|
||||
public ComponentName providerName;
|
||||
|
||||
/**
|
||||
* Indicates the restore status of the widget.
|
||||
|
||||
@@ -38,7 +38,7 @@ import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.launcher3.util.Thunk;
|
||||
|
||||
class LauncherClings implements OnClickListener, OnKeyListener {
|
||||
public class LauncherClings implements OnClickListener, OnKeyListener {
|
||||
private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
|
||||
|
||||
private static final String TAG_CROP_TOP_AND_SIDES = "crop_bg_top_and_sides";
|
||||
|
||||
@@ -3938,7 +3938,7 @@ public class Workspace extends PagedView
|
||||
});
|
||||
}
|
||||
|
||||
private View getFirstMatch(final ItemOperator operator) {
|
||||
public View getFirstMatch(final ItemOperator operator) {
|
||||
final View[] value = new View[1];
|
||||
mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
|
||||
@Override
|
||||
|
||||
@@ -207,4 +207,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getAccessibilityClassName() {
|
||||
return WidgetCell.class.getName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,30 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.SearchManager;
|
||||
import android.appwidget.AppWidgetHost;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageInstaller.SessionParams;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
import android.support.test.uiautomator.UiSelector;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
|
||||
import com.android.launcher3.compat.AppWidgetManagerCompat;
|
||||
import com.android.launcher3.compat.PackageInstallerCompat;
|
||||
import com.android.launcher3.ui.LauncherInstrumentationTestCase;
|
||||
import com.android.launcher3.util.ManagedProfileHeuristic;
|
||||
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
||||
import com.android.launcher3.widget.WidgetHostViewLoader;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Tests for bind widget flow.
|
||||
@@ -41,12 +33,8 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
*/
|
||||
@LargeTest
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public class BindWidgetTest extends InstrumentationTestCase {
|
||||
public class BindWidgetTest extends LauncherInstrumentationTestCase {
|
||||
|
||||
private static final long DEFAULT_TIMEOUT = 6000;
|
||||
|
||||
private UiDevice mDevice;
|
||||
private Context mTargetContext;
|
||||
private ContentResolver mResolver;
|
||||
private AppWidgetManagerCompat mWidgetManager;
|
||||
|
||||
@@ -59,23 +47,9 @@ public class BindWidgetTest extends InstrumentationTestCase {
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mDevice = UiDevice.getInstance(getInstrumentation());
|
||||
mTargetContext = getInstrumentation().getTargetContext();
|
||||
mResolver = mTargetContext.getContentResolver();
|
||||
mWidgetManager = AppWidgetManagerCompat.getInstance(mTargetContext);
|
||||
|
||||
// Check bind widget permission
|
||||
String pkg = mTargetContext.getPackageName();
|
||||
if (mTargetContext.getPackageManager().checkPermission(
|
||||
pkg, android.Manifest.permission.BIND_APPWIDGET)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(
|
||||
"appwidget grantbind --package " + pkg);
|
||||
// Read the input stream fully.
|
||||
FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
|
||||
while (fis.read() != -1);
|
||||
fis.close();
|
||||
}
|
||||
grantWidgetPermission();
|
||||
|
||||
// Clear all existing data
|
||||
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
|
||||
@@ -264,61 +238,14 @@ public class BindWidgetTest extends InstrumentationTestCase {
|
||||
throw new IllegalArgumentException(t);
|
||||
}
|
||||
// Launch the home activity
|
||||
getInstrumentation().getContext().startActivity(new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME)
|
||||
.setPackage(mTargetContext.getPackageName())
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
|
||||
startLauncher();
|
||||
// Verify UI
|
||||
UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
|
||||
.className(widgetClass);
|
||||
if (desc != null) {
|
||||
selector = selector.description(desc);
|
||||
}
|
||||
assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_TIMEOUT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a widget provider which can fit on the home screen.
|
||||
* @param hasConfigureScreen if true, a provider with a config screen is returned.
|
||||
*/
|
||||
private LauncherAppWidgetProviderInfo findWidgetProvider(final boolean hasConfigureScreen) {
|
||||
LauncherAppWidgetProviderInfo info = getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
|
||||
@Override
|
||||
public LauncherAppWidgetProviderInfo call() throws Exception {
|
||||
InvariantDeviceProfile idv =
|
||||
LauncherAppState.getInstance().getInvariantDeviceProfile();
|
||||
|
||||
ComponentName searchComponent = ((SearchManager) mTargetContext
|
||||
.getSystemService(Context.SEARCH_SERVICE)).getGlobalSearchActivity();
|
||||
String searchPackage = searchComponent == null
|
||||
? null : searchComponent.getPackageName();
|
||||
|
||||
for (AppWidgetProviderInfo info :
|
||||
AppWidgetManagerCompat.getInstance(mTargetContext).getAllProviders()) {
|
||||
if ((info.configure != null) ^ hasConfigureScreen) {
|
||||
continue;
|
||||
}
|
||||
// Exclude the widgets in search package, as Launcher already binds them in
|
||||
// QSB, so they can cause conflicts.
|
||||
if (info.provider.getPackageName().equals(searchPackage)) {
|
||||
continue;
|
||||
}
|
||||
LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
|
||||
.fromProviderInfo(mTargetContext, info);
|
||||
if (widgetInfo.minSpanX >= idv.numColumns
|
||||
|| widgetInfo.minSpanY >= idv.numRows) {
|
||||
continue;
|
||||
}
|
||||
return widgetInfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (info == null) {
|
||||
throw new IllegalArgumentException("No valid widget provider");
|
||||
}
|
||||
return info;
|
||||
assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,24 +324,6 @@ public class BindWidgetTest extends InstrumentationTestCase {
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the callback on the UI thread and returns the result.
|
||||
*/
|
||||
private <T> T getOnUiThread(final Callable<T> callback) {
|
||||
final AtomicReference<T> result = new AtomicReference<>(null);
|
||||
try {
|
||||
runTestOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
result.set(callback.call());
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) { }
|
||||
return result.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks the current thread until all the jobs in the main worker thread are complete.
|
||||
*/
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Point;
|
||||
import android.os.SystemClock;
|
||||
import android.support.test.uiautomator.By;
|
||||
import android.support.test.uiautomator.Direction;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
import android.support.test.uiautomator.UiObject2;
|
||||
import android.support.test.uiautomator.Until;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Add an arbitrary widget from the widget picker very quickly to test potential race conditions.
|
||||
*/
|
||||
@LargeTest
|
||||
public class QuickAddWidgetTest extends InstrumentationTestCase {
|
||||
// Disabled because it's flaky and not particularly useful. But this class could still be useful
|
||||
// as an example if we want other UI tests in the future.
|
||||
private static final boolean DISABLED = true;
|
||||
|
||||
private UiDevice mDevice;
|
||||
private String mTargetPackage;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mDevice = UiDevice.getInstance(getInstrumentation());
|
||||
|
||||
// Set Launcher3 as home.
|
||||
mTargetPackage = getInstrumentation().getTargetContext().getPackageName();
|
||||
Intent homeIntent = new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME)
|
||||
.setPackage(mTargetPackage)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
getInstrumentation().getContext().startActivity(homeIntent);
|
||||
mDevice.wait(Until.hasObject(By.pkg(mTargetPackage).depth(0)), 3000);
|
||||
}
|
||||
|
||||
public void testAddWidgetQuickly() throws Exception {
|
||||
if (DISABLED) return;
|
||||
mDevice.pressMenu(); // Enter overview mode.
|
||||
mDevice.wait(Until.findObject(By.text("Widgets")), 3000).click();
|
||||
UiObject2 calendarWidget = getWidgetByName("Clock");
|
||||
Point center = calendarWidget.getVisibleCenter();
|
||||
// Touch widget just long enough to pick it up (longPressTimeout), then let go immediately.
|
||||
getInstrumentation().sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(),
|
||||
SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, center.x, center.y, 0));
|
||||
Thread.sleep(ViewConfiguration.getLongPressTimeout() + 50);
|
||||
getInstrumentation().sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(),
|
||||
SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, center.x, center.y, 0));
|
||||
|
||||
assertTrue("Drag was never started", isOnHomescreen());
|
||||
}
|
||||
|
||||
private UiObject2 getWidgetByName(String name) {
|
||||
UiObject2 widgetsList = mDevice.wait(Until.findObject(By.res(mTargetPackage,
|
||||
"widgets_list_view")), 3000);
|
||||
do {
|
||||
UiObject2 widget = getVisibleWidgetByName(name);
|
||||
if (widget != null) {
|
||||
return widget;
|
||||
}
|
||||
} while (widgetsList.scroll(Direction.DOWN, 1f));
|
||||
return getVisibleWidgetByName(name);
|
||||
}
|
||||
|
||||
private UiObject2 getVisibleWidgetByName(String name) {
|
||||
List<UiObject2> visibleWidgets = mDevice.wait(Until.findObjects(By.clazz(
|
||||
"android.widget.LinearLayout")), 3000);
|
||||
for (UiObject2 widget : visibleWidgets) {
|
||||
if (widget.hasObject(By.text(name))) {
|
||||
return widget;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isOnHomescreen() {
|
||||
return mDevice.wait(Until.hasObject(By.res(mTargetPackage, "hotseat")), 3000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.android.launcher3.ui;
|
||||
|
||||
import android.support.test.uiautomator.By;
|
||||
import android.support.test.uiautomator.UiObject2;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.Workspace.ItemOperator;
|
||||
import com.android.launcher3.util.Condition;
|
||||
import com.android.launcher3.util.Wait;
|
||||
import com.android.launcher3.widget.WidgetCell;
|
||||
|
||||
/**
|
||||
* Test to add widget from widget tray
|
||||
*/
|
||||
@LargeTest
|
||||
public class AddWidgetTest extends LauncherInstrumentationTestCase {
|
||||
|
||||
private LauncherAppWidgetProviderInfo widgetInfo;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
widgetInfo = findWidgetProvider(false /* hasConfigureScreen */);
|
||||
}
|
||||
|
||||
public void testDragIcon_portrait() throws Throwable {
|
||||
lockRotation(true);
|
||||
performTest();
|
||||
}
|
||||
|
||||
public void testDragIcon_landscape() throws Throwable {
|
||||
lockRotation(false);
|
||||
performTest();
|
||||
}
|
||||
|
||||
private void performTest() throws Throwable {
|
||||
clearHomescreen();
|
||||
Launcher launcher = startLauncher();
|
||||
|
||||
// Open all apps and wait for load complete.
|
||||
final UiObject2 widgetContainer = openWidgetsTray();
|
||||
assertTrue(Wait.atMost(Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT));
|
||||
|
||||
// Drag widget to homescreen
|
||||
UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
|
||||
.hasDescendant(By.text(widgetInfo.getLabel(mTargetContext.getPackageManager()))));
|
||||
dragToWorkspace(widget);
|
||||
|
||||
assertNotNull(launcher.getWorkspace().getFirstMatch(new ItemOperator() {
|
||||
@Override
|
||||
public boolean evaluate(ItemInfo info, View view) {
|
||||
return info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).providerName.equals(widgetInfo.provider);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.android.launcher3.ui;
|
||||
|
||||
import android.support.test.uiautomator.By;
|
||||
import android.support.test.uiautomator.UiObject2;
|
||||
import android.support.test.uiautomator.Until;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
|
||||
import com.android.launcher3.compat.LauncherActivityInfoCompat;
|
||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||
import com.android.launcher3.compat.UserHandleCompat;
|
||||
import com.android.launcher3.util.Condition;
|
||||
import com.android.launcher3.util.Wait;
|
||||
|
||||
/**
|
||||
* Test for verifying apps is launched from all-apps
|
||||
*/
|
||||
@LargeTest
|
||||
public class AllAppsAppLaunchTest extends LauncherInstrumentationTestCase {
|
||||
|
||||
private LauncherActivityInfoCompat mSettingsApp;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
|
||||
.getActivityList("com.android.settings", UserHandleCompat.myUserHandle()).get(0);
|
||||
}
|
||||
|
||||
public void testAppLauncher_portrait() throws Exception {
|
||||
lockRotation(true);
|
||||
performTest();
|
||||
}
|
||||
|
||||
public void testAppLauncher_landscape() throws Exception {
|
||||
lockRotation(false);
|
||||
performTest();
|
||||
}
|
||||
|
||||
private void performTest() throws Exception {
|
||||
startLauncher();
|
||||
|
||||
// Open all apps and wait for load complete
|
||||
final UiObject2 appsContainer = openAllApps();
|
||||
assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
|
||||
|
||||
// Open settings app and verify app launched
|
||||
scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString())).click();
|
||||
assertTrue(mDevice.wait(Until.hasObject(By.pkg(
|
||||
mSettingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.android.launcher3.ui;
|
||||
|
||||
import android.support.test.uiautomator.By;
|
||||
import android.support.test.uiautomator.UiObject2;
|
||||
import android.support.test.uiautomator.Until;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
|
||||
import com.android.launcher3.compat.LauncherActivityInfoCompat;
|
||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||
import com.android.launcher3.compat.UserHandleCompat;
|
||||
import com.android.launcher3.util.Condition;
|
||||
import com.android.launcher3.util.Wait;
|
||||
|
||||
/**
|
||||
* Test for dragging an icon from all-apps to homescreen.
|
||||
*/
|
||||
@LargeTest
|
||||
public class AllAppsIconToHomeTest extends LauncherInstrumentationTestCase {
|
||||
|
||||
private LauncherActivityInfoCompat mSettingsApp;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
|
||||
.getActivityList("com.android.settings", UserHandleCompat.myUserHandle()).get(0);
|
||||
}
|
||||
|
||||
public void testDragIcon_portrait() throws Throwable {
|
||||
lockRotation(true);
|
||||
performTest();
|
||||
}
|
||||
|
||||
public void testDragIcon_landscape() throws Throwable {
|
||||
lockRotation(false);
|
||||
performTest();
|
||||
}
|
||||
|
||||
private void performTest() throws Throwable {
|
||||
clearHomescreen();
|
||||
startLauncher();
|
||||
|
||||
// Open all apps and wait for load complete.
|
||||
final UiObject2 appsContainer = openAllApps();
|
||||
assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
|
||||
|
||||
// Drag icon to homescreen.
|
||||
UiObject2 icon = scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString()));
|
||||
dragToWorkspace(icon);
|
||||
|
||||
// Verify that the icon works on homescreen.
|
||||
mDevice.findObject(By.text(mSettingsApp.getLabel().toString())).click();
|
||||
assertTrue(mDevice.wait(Until.hasObject(By.pkg(
|
||||
mSettingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
package com.android.launcher3.ui;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Point;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.support.test.uiautomator.By;
|
||||
import android.support.test.uiautomator.BySelector;
|
||||
import android.support.test.uiautomator.Direction;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
import android.support.test.uiautomator.UiObject2;
|
||||
import android.support.test.uiautomator.Until;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.LauncherClings;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.compat.AppWidgetManagerCompat;
|
||||
import com.android.launcher3.util.ManagedProfileHeuristic;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Base class for all instrumentation tests providing various utility methods.
|
||||
*/
|
||||
public class LauncherInstrumentationTestCase extends InstrumentationTestCase {
|
||||
|
||||
public static final long DEFAULT_UI_TIMEOUT = 3000;
|
||||
|
||||
protected UiDevice mDevice;
|
||||
protected Context mTargetContext;
|
||||
protected String mTargetPackage;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mDevice = UiDevice.getInstance(getInstrumentation());
|
||||
mTargetContext = getInstrumentation().getTargetContext();
|
||||
mTargetPackage = mTargetContext.getPackageName();
|
||||
}
|
||||
|
||||
protected void lockRotation(boolean naturalOrientation) throws RemoteException {
|
||||
Utilities.getPrefs(mTargetContext)
|
||||
.edit()
|
||||
.putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, !naturalOrientation)
|
||||
.commit();
|
||||
|
||||
if (naturalOrientation) {
|
||||
mDevice.setOrientationNatural();
|
||||
} else {
|
||||
mDevice.setOrientationRight();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the launcher activity in the target package and returns the Launcher instance.
|
||||
*/
|
||||
protected Launcher startLauncher() {
|
||||
Intent homeIntent = new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME)
|
||||
.setPackage(mTargetPackage)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
return (Launcher) getInstrumentation().startActivitySync(homeIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants the launcher permission to bind widgets.
|
||||
*/
|
||||
protected void grantWidgetPermission() throws IOException {
|
||||
// Check bind widget permission
|
||||
if (mTargetContext.getPackageManager().checkPermission(
|
||||
mTargetPackage, android.Manifest.permission.BIND_APPWIDGET)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(
|
||||
"appwidget grantbind --package " + mTargetPackage);
|
||||
// Read the input stream fully.
|
||||
FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
|
||||
while (fis.read() != -1);
|
||||
fis.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens all apps and returns the recycler view
|
||||
*/
|
||||
protected UiObject2 openAllApps() {
|
||||
mDevice.wait(Until.findObject(
|
||||
By.desc(mTargetContext.getString(R.string.all_apps_button_label))), DEFAULT_UI_TIMEOUT).click();
|
||||
return findViewById(R.id.apps_list_view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens widget tray and returns the recycler view.
|
||||
*/
|
||||
protected UiObject2 openWidgetsTray() {
|
||||
mDevice.pressMenu(); // Enter overview mode.
|
||||
mDevice.wait(Until.findObject(
|
||||
By.text(mTargetContext.getString(R.string.widget_button_text)
|
||||
.toUpperCase(Locale.getDefault()))), DEFAULT_UI_TIMEOUT).click();
|
||||
return findViewById(R.id.widgets_list_view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the {@param container} until it finds an object matching {@param condition}.
|
||||
* @return the matching object.
|
||||
*/
|
||||
protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) {
|
||||
do {
|
||||
UiObject2 widget = container.findObject(condition);
|
||||
if (widget != null) {
|
||||
return widget;
|
||||
}
|
||||
} while (container.scroll(Direction.DOWN, 1f));
|
||||
return container.findObject(condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drags an icon to the center of homescreen.
|
||||
*/
|
||||
protected void dragToWorkspace(UiObject2 icon) {
|
||||
Point center = icon.getVisibleCenter();
|
||||
|
||||
// Action Down
|
||||
sendPointer(MotionEvent.ACTION_DOWN, center);
|
||||
|
||||
// Wait until "Remove/Delete target is visible
|
||||
assertNotNull(findViewById(R.id.delete_target_text));
|
||||
|
||||
Point moveLocation = findViewById(R.id.drag_layer).getVisibleCenter();
|
||||
|
||||
// Move to center
|
||||
while(!moveLocation.equals(center)) {
|
||||
center.x = getNextMoveValue(moveLocation.x, center.x);
|
||||
center.y = getNextMoveValue(moveLocation.y, center.y);
|
||||
sendPointer(MotionEvent.ACTION_MOVE, center);
|
||||
}
|
||||
sendPointer(MotionEvent.ACTION_UP, center);
|
||||
|
||||
// Wait until remove target is gone.
|
||||
mDevice.wait(Until.gone(getSelectorForId(R.id.delete_target_text)), DEFAULT_UI_TIMEOUT);
|
||||
}
|
||||
|
||||
private int getNextMoveValue(int targetValue, int oldValue) {
|
||||
if (targetValue - oldValue > 10) {
|
||||
return oldValue + 10;
|
||||
} else if (targetValue - oldValue < -10) {
|
||||
return oldValue - 10;
|
||||
} else {
|
||||
return targetValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void sendPointer(int action, Point point) {
|
||||
MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
|
||||
SystemClock.uptimeMillis(), action, point.x, point.y, 0);
|
||||
getInstrumentation().sendPointerSync(event);
|
||||
event.recycle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all icons from homescreen and hotseat.
|
||||
*/
|
||||
public void clearHomescreen() throws Throwable {
|
||||
LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
|
||||
LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
|
||||
LauncherClings.markFirstRunClingDismissed(mTargetContext);
|
||||
ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
|
||||
|
||||
runTestOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Reset the loader state
|
||||
LauncherAppState.getInstance().getModel().resetLoadedState(true, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the callback on the UI thread and returns the result.
|
||||
*/
|
||||
protected <T> T getOnUiThread(final Callable<T> callback) {
|
||||
final AtomicReference<T> result = new AtomicReference<>(null);
|
||||
try {
|
||||
runTestOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
result.set(callback.call());
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) { }
|
||||
return result.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a widget provider which can fit on the home screen.
|
||||
* @param hasConfigureScreen if true, a provider with a config screen is returned.
|
||||
*/
|
||||
protected LauncherAppWidgetProviderInfo findWidgetProvider(final boolean hasConfigureScreen) {
|
||||
LauncherAppWidgetProviderInfo info = getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
|
||||
@Override
|
||||
public LauncherAppWidgetProviderInfo call() throws Exception {
|
||||
InvariantDeviceProfile idv =
|
||||
LauncherAppState.getInstance().getInvariantDeviceProfile();
|
||||
|
||||
ComponentName searchComponent = ((SearchManager) mTargetContext
|
||||
.getSystemService(Context.SEARCH_SERVICE)).getGlobalSearchActivity();
|
||||
String searchPackage = searchComponent == null
|
||||
? null : searchComponent.getPackageName();
|
||||
|
||||
for (AppWidgetProviderInfo info :
|
||||
AppWidgetManagerCompat.getInstance(mTargetContext).getAllProviders()) {
|
||||
if ((info.configure != null) ^ hasConfigureScreen) {
|
||||
continue;
|
||||
}
|
||||
// Exclude the widgets in search package, as Launcher already binds them in
|
||||
// QSB, so they can cause conflicts.
|
||||
if (info.provider.getPackageName().equals(searchPackage)) {
|
||||
continue;
|
||||
}
|
||||
LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
|
||||
.fromProviderInfo(mTargetContext, info);
|
||||
if (widgetInfo.minSpanX >= idv.numColumns
|
||||
|| widgetInfo.minSpanY >= idv.numRows) {
|
||||
continue;
|
||||
}
|
||||
return widgetInfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (info == null) {
|
||||
throw new IllegalArgumentException("No valid widget provider");
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
protected UiObject2 findViewById(int id) {
|
||||
return mDevice.wait(Until.findObject(getSelectorForId(id)), DEFAULT_UI_TIMEOUT);
|
||||
}
|
||||
|
||||
protected BySelector getSelectorForId(int id) {
|
||||
String name = mTargetContext.getResources().getResourceEntryName(id);
|
||||
return By.res(mTargetPackage, name);
|
||||
}
|
||||
}
|
||||
+7
-20
@@ -1,24 +1,20 @@
|
||||
package com.android.launcher3;
|
||||
package com.android.launcher3.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Rect;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
import android.support.test.uiautomator.UiObject;
|
||||
import android.support.test.uiautomator.UiSelector;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
/**
|
||||
* Test for auto rotate preference.
|
||||
*/
|
||||
@MediumTest
|
||||
public class RotationPreferenceTest extends InstrumentationTestCase {
|
||||
|
||||
private UiDevice mDevice;
|
||||
private Context mTargetContext;
|
||||
private String mTargetPackage;
|
||||
public class RotationPreferenceTest extends LauncherInstrumentationTestCase {
|
||||
|
||||
private SharedPreferences mPrefs;
|
||||
private boolean mOriginalRotationValue;
|
||||
@@ -48,7 +44,7 @@ public class RotationPreferenceTest extends InstrumentationTestCase {
|
||||
|
||||
setRotationEnabled(false);
|
||||
mDevice.setOrientationRight();
|
||||
goToLauncher();
|
||||
startLauncher();
|
||||
|
||||
Rect hotseat = getHotseatBounds();
|
||||
assertTrue(hotseat.width() > hotseat.height());
|
||||
@@ -62,21 +58,12 @@ public class RotationPreferenceTest extends InstrumentationTestCase {
|
||||
|
||||
setRotationEnabled(true);
|
||||
mDevice.setOrientationRight();
|
||||
goToLauncher();
|
||||
startLauncher();
|
||||
|
||||
Rect hotseat = getHotseatBounds();
|
||||
assertTrue(hotseat.width() < hotseat.height());
|
||||
}
|
||||
|
||||
private void goToLauncher() {
|
||||
Intent homeIntent = new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME)
|
||||
.setPackage(mTargetPackage)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
getInstrumentation().getContext().startActivity(homeIntent);
|
||||
mDevice.findObject(new UiSelector().packageName(mTargetPackage)).waitForExists(6000);
|
||||
}
|
||||
|
||||
private void setRotationEnabled(boolean enabled) {
|
||||
mPrefs.edit().putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, enabled).commit();
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.android.launcher3.util;
|
||||
|
||||
import android.support.test.uiautomator.UiObject2;
|
||||
|
||||
import com.android.launcher3.MainThreadExecutor;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public abstract class Condition {
|
||||
|
||||
public abstract boolean isTrue() throws Throwable;
|
||||
|
||||
/**
|
||||
* Converts the condition to be run on UI thread.
|
||||
*/
|
||||
public static Condition runOnUiThread(final Condition condition) {
|
||||
final MainThreadExecutor executor = new MainThreadExecutor();
|
||||
return new Condition() {
|
||||
@Override
|
||||
public boolean isTrue() throws Throwable {
|
||||
final AtomicBoolean value = new AtomicBoolean(false);
|
||||
final Throwable[] exceptions = new Throwable[1];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
value.set(condition.isTrue());
|
||||
} catch (Throwable e) {
|
||||
exceptions[0] = e;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
latch.await(1, TimeUnit.SECONDS);
|
||||
if (exceptions[0] != null) {
|
||||
throw exceptions[0];
|
||||
}
|
||||
return value.get();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Condition minChildCount(final UiObject2 obj, final int childCount) {
|
||||
return new Condition() {
|
||||
@Override
|
||||
public boolean isTrue() {
|
||||
return obj.getChildCount() >= childCount;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.android.launcher3.util;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
/**
|
||||
* A utility class for waiting for a condition to be true.
|
||||
*/
|
||||
public class Wait {
|
||||
|
||||
private static final long DEFAULT_SLEEP_MS = 200;
|
||||
|
||||
public static boolean atMost(Condition condition, long timeout) {
|
||||
return atMost(condition, timeout, DEFAULT_SLEEP_MS);
|
||||
}
|
||||
|
||||
public static boolean atMost(Condition condition, long timeout, long sleepMillis) {
|
||||
long endTime = SystemClock.uptimeMillis() + timeout;
|
||||
while (SystemClock.uptimeMillis() < endTime) {
|
||||
try {
|
||||
if (condition.isTrue()) {
|
||||
return true;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// Ignore
|
||||
}
|
||||
SystemClock.sleep(sleepMillis);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user