Home | History | Annotate | Download | only in helpers
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of 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,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.system.helpers;
     18 
     19 import android.app.Instrumentation;
     20 import android.content.ComponentName;
     21 import android.content.Intent;
     22 import android.content.pm.PackageInfo;
     23 import android.content.pm.PackageManager;
     24 import android.content.res.Resources;
     25 import android.graphics.Point;
     26 import android.graphics.Rect;
     27 import android.os.SystemClock;
     28 import android.support.test.InstrumentationRegistry;
     29 import android.support.test.uiautomator.By;
     30 import android.support.test.uiautomator.UiDevice;
     31 import android.support.test.uiautomator.UiObject2;
     32 import android.support.test.uiautomator.UiObjectNotFoundException;
     33 import android.support.test.uiautomator.Until;
     34 import android.util.Log;
     35 
     36 import org.junit.Assert;
     37 
     38 import java.io.IOException;
     39 import java.util.ArrayList;
     40 import java.util.List;
     41 
     42 /**
     43  * Implement common helper methods for Overview.
     44  */
     45 public class OverviewHelper {
     46 
     47     private static final String TAG = OverviewHelper.class.getSimpleName();
     48     private static final int TIMEOUT = 3000;
     49     private static final String RECENTS = "com.android.systemui:id/recents_view";
     50 
     51     private UiDevice mDevice = null;
     52     private Instrumentation mInstrumentation = null;
     53     private ActivityHelper mActHelper = null;
     54     private final CommandsHelper mCommandsHelper;
     55     public static OverviewHelper sInstance = null;
     56 
     57     public OverviewHelper() {
     58         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
     59         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     60         mActHelper = ActivityHelper.getInstance();
     61         mCommandsHelper = CommandsHelper.getInstance(mInstrumentation);
     62     }
     63 
     64     public static OverviewHelper getInstance() {
     65         if (sInstance == null) {
     66             sInstance = new OverviewHelper();
     67         }
     68         return sInstance;
     69     }
     70 
     71     /**
     72      * Navigates to the recents screen
     73      * @returns recents object
     74      * @throws UiObjectNotFoundException
     75      */
     76     public UiObject2 navigateToRecents() throws Exception {
     77         mDevice.pressRecentApps();
     78         mDevice.waitForIdle();
     79         return mDevice.wait(Until.findObject(By.res(RECENTS)), TIMEOUT);
     80     }
     81 
     82     /**
     83      * Populates recents by launching six apps
     84      * @throws InterruptedException
     85      */
     86     public void populateRecents() throws InterruptedException {
     87         // We launch six apps, since five is the maximum number
     88         // of apps under Recents
     89         String[] appPackages = {"com.google.android.gm",
     90                 "com.google.android.deskclock", "com.android.settings",
     91                 "com.google.android.youtube", "com.google.android.contacts",
     92                 "com.google.android.apps.maps"};
     93         for (String appPackage : appPackages) {
     94             mActHelper.launchPackage(appPackage);
     95         }
     96     }
     97 
     98     public ArrayList<String> populateManyRecentApps() throws IOException {
     99         PackageManager pm = mInstrumentation.getContext().getPackageManager();
    100         List<PackageInfo> packages = pm.getInstalledPackages(0);
    101         ArrayList<String> launchedPackages = new ArrayList<>();
    102         for (PackageInfo pkg : packages) {
    103             if (pkg.packageName.equals(mInstrumentation.getTargetContext().getPackageName())) {
    104                 continue;
    105             }
    106             Intent intent = pm.getLaunchIntentForPackage(pkg.packageName);
    107             if (intent == null) {
    108                 continue;
    109             }
    110             intent.addCategory(Intent.CATEGORY_LAUNCHER);
    111             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    112             try {
    113                 mInstrumentation.getTargetContext().startActivity(intent);
    114             } catch (SecurityException e) {
    115                 Log.i(TAG, "Failed to start package " + pkg.packageName + ", exception: " + e);
    116             }
    117 
    118             // Don't overload the system
    119             SystemClock.sleep(500);
    120             launchedPackages.add(pkg.packageName);
    121         }
    122 
    123         // Give the apps some time to finish starting. Some apps start another activity while
    124         // starting, and we don't want to happen when we are testing stuff.
    125         SystemClock.sleep(3000);
    126 
    127         // Close any crash dialogs
    128         while (mDevice.hasObject(By.textContains("has stopped"))) {
    129             UiObject2 crashDialog = mDevice.findObject(By.text("Close"));
    130             if (crashDialog != null) {
    131                 crashDialog.clickAndWait(Until.newWindow(), 2000);
    132             }
    133         }
    134         return launchedPackages;
    135     }
    136 
    137     public void forceStopPackages(ArrayList<String> packages) {
    138         for (String pkg : packages) {
    139             mCommandsHelper.executeShellCommand("am force-stop " + pkg);
    140         }
    141     }
    142 
    143     /**
    144     * Scrolls through given recents object to the top
    145     * @param recents Recents object
    146     */
    147     public void scrollToTopOfRecents(UiObject2 recents) {
    148         Rect r = recents.getVisibleBounds();
    149         // decide the top & bottom edges for scroll gesture
    150         int top = r.top + r.height() / 4; // top edge = top + 25% height
    151         int bottom = r.bottom - 200; // bottom edge = bottom & shift up 200px
    152         mDevice.swipe(r.width() / 2, top, r.width() / 2, bottom, 5);
    153         mDevice.waitForIdle();
    154     }
    155 
    156     /**
    157      * Docks an app to the top half of the multiwindow screen
    158      * @param appPackageName name of app package
    159      * @param appName Name of app to verify on screen
    160      * @throws UiObjectNotFoundException, InterruptedException
    161      */
    162     public void dockAppToTopMultiwindowSlot(String appPackageName, String appName)
    163             throws Exception {
    164         mDevice.pressRecentApps();
    165         mDevice.waitForIdle();
    166         UiObject2 recentsView = mDevice.wait(Until.findObject
    167                 (By.res("com.android.systemui:id/recents_view")),TIMEOUT);
    168         // Check if recents isn't already empty, if not, clear it.
    169         if (!mDevice.wait(Until.hasObject(By.text("No recent items")),TIMEOUT)) {
    170             scrollToTopOfRecents(recentsView);
    171             // click clear all
    172             UiObject2 clearAll = mDevice.wait(Until.findObject(By.text("CLEAR ALL")),TIMEOUT);
    173             if (!clearAll.equals(null)) {
    174                 clearAll.click();
    175             }
    176             Thread.sleep(TIMEOUT);
    177         }
    178         // Open app
    179         mActHelper.launchPackage(appPackageName);
    180         // Go to overview
    181         mDevice.pressRecentApps();
    182         mDevice.waitForIdle();
    183         // Long press on app
    184         UiObject2 appObject = mDevice.wait(Until.findObject
    185                 (By.desc(appName)),TIMEOUT);
    186         int yCoordinate = mDevice.getDisplayHeight() / 12;
    187         int xCoordinate = mDevice.getDisplayWidth() / 2;
    188         // Drag and drop the app object to the multiwindow area
    189         appObject.drag(new Point(xCoordinate, yCoordinate), 1000);
    190         // Adding a sleep to allow the drag and drop animation to finish.
    191         Thread.sleep(TIMEOUT);
    192         mDevice.click(mDevice.getDisplayHeight() / 4, mDevice.getDisplayWidth() / 2);
    193         Assert.assertTrue("App not correctly docked to top multiwindow slot",
    194                 mDevice.wait(Until.hasObject(By.pkg(appPackageName)
    195                         .res("android:id/content")), TIMEOUT));
    196     }
    197 
    198     /**
    199      * Docks two apps, one to the each half of the multiwindow screen
    200      * @param topAppPackageName name of app package for top half
    201      * @param topAppName Name of top app to verify on screen
    202      * @param bottomAppPackageName name of app package for bottom half
    203      * @throws UiObjectNotFoundException, InterruptedException
    204      */
    205     public void dockAppsToBothMultiwindowAreas(String topAppPackageName,
    206             String topAppName, String bottomAppPackageName) throws Exception {
    207         dockAppToTopMultiwindowSlot(topAppPackageName, topAppName);
    208         mDevice.pressHome();
    209         mDevice.waitForIdle();
    210         // After docking the top app, simply launching another app
    211         // will launch it in the bottom half in docked mode. This
    212         // results in two apps being docked to multiwindow.
    213         mActHelper.launchPackage(bottomAppPackageName);
    214     }
    215 
    216     /**
    217      * Undocks apps from multiwindow. Only the package for the upper app is needed.
    218      * @param topAppPackageName name of app package for top half
    219      * @throws UiObjectNotFoundException, InterruptedException
    220      */
    221     public void undockAppFromMultiwindow(String topAppPackageName) throws Exception {
    222         mDevice.click(mDevice.getDisplayHeight() / 4, mDevice.getDisplayWidth() / 2);
    223         UiObject2 appArea = mDevice.wait(Until.findObject(By.pkg(topAppPackageName)
    224                 .res("android:id/content")), TIMEOUT);
    225         Rect appBounds = appArea.getVisibleBounds();
    226         int xCoordinate = mDevice.getDisplayWidth() / 2;
    227         mDevice.drag(xCoordinate, appBounds.bottom, xCoordinate,
    228                 mDevice.getDisplayHeight() - 120, 4);
    229         // Adding a sleep to allow the drag and drop animation to finish.
    230         Thread.sleep(TIMEOUT);
    231     }
    232 
    233     /**
    234      * Returns whether recents (overview) is implemented in Launcher.
    235      */
    236     public static boolean isRecentsInLauncher() {
    237         final PackageManager pm = InstrumentationRegistry.getInstrumentation().getTargetContext()
    238                 .getPackageManager();
    239         final Resources res = Resources.getSystem();
    240         int id = res.getIdentifier("config_recentsComponentName", "string", "android");
    241         ComponentName recentsComponent = ComponentName.unflattenFromString(res.getString(id));
    242         Intent intent = new Intent("android.intent.action.QUICKSTEP_SERVICE")
    243                 .setPackage(recentsComponent.getPackageName());
    244         return pm.resolveService(intent, 0) != null;
    245     }
    246 }