diff options
Diffstat (limited to 'tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java')
-rw-r--r-- | tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java new file mode 100644 index 000000000..7fb5d8521 --- /dev/null +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.launcher3.ui; + +import android.app.Instrumentation; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.LauncherActivityInfo; +import android.graphics.Point; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; +import android.support.test.InstrumentationRegistry; +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.view.MotionEvent; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AppWidgetManagerCompat; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.testcomponent.AppWidgetNoConfig; +import com.android.launcher3.testcomponent.AppWidgetWithConfig; +import com.android.launcher3.util.ManagedProfileHeuristic; + +import org.junit.Before; + +import java.util.Locale; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * Base class for all instrumentation tests providing various utility methods. + */ +public abstract class AbstractLauncherUiTest { + + public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10); + public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5; + + public static final long DEFAULT_UI_TIMEOUT = 3000; + public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5; + + protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); + protected UiDevice mDevice; + protected Context mTargetContext; + protected String mTargetPackage; + + @Before + public void setUp() throws Exception { + mDevice = UiDevice.getInstance(getInstrumentation()); + mTargetContext = InstrumentationRegistry.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(); + } + } + + protected Instrumentation getInstrumentation() { + return InstrumentationRegistry.getInstrumentation(); + } + + /** + * Opens all apps and returns the recycler view + */ + protected UiObject2 openAllApps() { + mDevice.waitForIdle(); + if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { + // clicking on the page indicator brings up all apps tray on non tablets. + findViewById(R.id.page_indicator).click(); + } else { + 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, boolean expectedToShowShortcuts) { + Point center = icon.getVisibleCenter(); + + // Action Down + sendPointer(MotionEvent.ACTION_DOWN, center); + + UiObject2 dragLayer = findViewById(R.id.drag_layer); + + if (expectedToShowShortcuts) { + // Make sure shortcuts show up, and then move a bit to hide them. + assertNotNull(findViewById(R.id.deep_shortcuts_container)); + + Point moveLocation = new Point(center); + int distanceToMove = mTargetContext.getResources().getDimensionPixelSize( + R.dimen.deep_shortcuts_start_drag_threshold) + 50; + if (moveLocation.y - distanceToMove >= dragLayer.getVisibleBounds().top) { + moveLocation.y -= distanceToMove; + } else { + moveLocation.y += distanceToMove; + } + movePointer(center, moveLocation); + + assertNull(findViewById(R.id.deep_shortcuts_container)); + } + + // Wait until Remove/Delete target is visible + assertNotNull(findViewById(R.id.delete_target_text)); + + Point moveLocation = dragLayer.getVisibleCenter(); + + // Move to center + movePointer(center, moveLocation); + 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 void movePointer(Point from, Point to) { + while(!from.equals(to)) { + from.x = getNextMoveValue(to.x, from.x); + from.y = getNextMoveValue(to.y, from.y); + sendPointer(MotionEvent.ACTION_MOVE, from); + } + } + + 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; + } + } + + protected 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); + resetLoaderState(); + } + + protected void resetLoaderState() { + try { + mMainThreadExecutor.execute(new Runnable() { + @Override + public void run() { + ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext); + LauncherAppState.getInstance(mTargetContext).getModel().forceReload(); + } + }); + } catch (Throwable t) { + throw new IllegalArgumentException(t); + } + } + + /** + * Runs the callback on the UI thread and returns the result. + */ + protected <T> T getOnUiThread(final Callable<T> callback) { + try { + return mMainThreadExecutor.submit(callback).get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 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 { + ComponentName cn = new ComponentName(getInstrumentation().getContext(), + hasConfigureScreen ? AppWidgetWithConfig.class : AppWidgetNoConfig.class); + return AppWidgetManagerCompat.getInstance(mTargetContext) + .findProvider(cn, Process.myUserHandle()); + } + }); + 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); + } + + protected LauncherActivityInfo getSettingsApp() { + return LauncherAppsCompat.getInstance(mTargetContext) + .getActivityList("com.android.settings", Process.myUserHandle()).get(0); + } + + /** + * Broadcast receiver which blocks until the result is received. + */ + public class BlockingBroadcastReceiver extends BroadcastReceiver { + + private final CountDownLatch latch = new CountDownLatch(1); + private Intent mIntent; + + public BlockingBroadcastReceiver(String action) { + mTargetContext.registerReceiver(this, new IntentFilter(action)); + } + + @Override + public void onReceive(Context context, Intent intent) { + mIntent = intent; + latch.countDown(); + } + + public Intent blockingGetIntent() throws InterruptedException { + latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS); + mTargetContext.unregisterReceiver(this); + return mIntent; + } + + public Intent blockingGetExtraIntent() throws InterruptedException { + Intent intent = blockingGetIntent(); + return intent == null ? null : (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT); + } + } +} |