Home | History | Annotate | Download | only in ui
      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
     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 
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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 
    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 
    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      */
    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      */
    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      */
    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 
    265     protected UiObject2 findViewById(int id) {
    266         return mDevice.wait(Until.findObject(getSelectorForId(id)), DEFAULT_UI_TIMEOUT);
    267     }
    268 
    269     protected BySelector getSelectorForId(int id) {
    270         String name = mTargetContext.getResources().getResourceEntryName(id);
    271         return By.res(mTargetPackage, name);
    272     }
    273 }
    274