Home | History | Annotate | Download | only in widget
      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.widget;
     17 
     18 import android.app.Activity;
     19 import android.app.Application;
     20 import android.app.PendingIntent;
     21 import android.appwidget.AppWidgetManager;
     22 import android.content.Intent;
     23 import android.graphics.Color;
     24 import android.support.test.uiautomator.By;
     25 import android.support.test.uiautomator.UiObject2;
     26 import android.support.test.uiautomator.Until;
     27 import android.test.suitebuilder.annotation.LargeTest;
     28 import android.view.View;
     29 
     30 import com.android.launcher3.ItemInfo;
     31 import com.android.launcher3.Launcher;
     32 import com.android.launcher3.LauncherAppWidgetInfo;
     33 import com.android.launcher3.LauncherSettings.Favorites;
     34 import com.android.launcher3.MainThreadExecutor;
     35 import com.android.launcher3.R;
     36 import com.android.launcher3.ShortcutInfo;
     37 import com.android.launcher3.Utilities;
     38 import com.android.launcher3.Workspace.ItemOperator;
     39 import com.android.launcher3.shortcuts.ShortcutKey;
     40 import com.android.launcher3.testcomponent.AppWidgetNoConfig;
     41 import com.android.launcher3.testcomponent.AppWidgetWithConfig;
     42 import com.android.launcher3.testcomponent.RequestPinItemActivity;
     43 import com.android.launcher3.ui.LauncherInstrumentationTestCase;
     44 import com.android.launcher3.util.Condition;
     45 import com.android.launcher3.util.SimpleActivityMonitor;
     46 import com.android.launcher3.util.Wait;
     47 import com.android.launcher3.widget.WidgetCell;
     48 
     49 import java.util.UUID;
     50 import java.util.concurrent.Callable;
     51 
     52 /**
     53  * Test to verify pin item request flow.
     54  */
     55 @LargeTest
     56 public class RequestPinItemTest  extends LauncherInstrumentationTestCase {
     57 
     58     private SimpleActivityMonitor mActivityMonitor;
     59     private MainThreadExecutor mMainThreadExecutor;
     60 
     61     private String mCallbackAction;
     62     private String mShortcutId;
     63     private int mAppWidgetId;
     64 
     65     @Override
     66     protected void setUp() throws Exception {
     67         super.setUp();
     68         grantWidgetPermission();
     69         setDefaultLauncher();
     70 
     71         mActivityMonitor = new SimpleActivityMonitor();
     72         ((Application) getInstrumentation().getTargetContext().getApplicationContext())
     73                 .registerActivityLifecycleCallbacks(mActivityMonitor);
     74         mMainThreadExecutor = new MainThreadExecutor();
     75 
     76         mCallbackAction = UUID.randomUUID().toString();
     77         mShortcutId = UUID.randomUUID().toString();
     78     }
     79 
     80     @Override
     81     protected void tearDown() throws Exception {
     82         ((Application) getInstrumentation().getTargetContext().getApplicationContext())
     83                 .unregisterActivityLifecycleCallbacks(mActivityMonitor);
     84         super.tearDown();
     85     }
     86 
     87     public void testPinWidgetNoConfig() throws Throwable {
     88         runTest("pinWidgetNoConfig", true, new ItemOperator() {
     89             @Override
     90             public boolean evaluate(ItemInfo info, View view) {
     91                 return info instanceof LauncherAppWidgetInfo &&
     92                         ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
     93                         ((LauncherAppWidgetInfo) info).providerName.getClassName()
     94                                 .equals(AppWidgetNoConfig.class.getName());
     95             }
     96         });
     97     }
     98 
     99     public void testPinWidgetNoConfig_customPreview() throws Throwable {
    100         // Command to set custom preview
    101         Intent command =  RequestPinItemActivity.getCommandIntent(
    102                 RequestPinItemActivity.class, "setRemoteViewColor").putExtra(
    103                 RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED);
    104 
    105         runTest("pinWidgetNoConfig", true, new ItemOperator() {
    106             @Override
    107             public boolean evaluate(ItemInfo info, View view) {
    108                 return info instanceof LauncherAppWidgetInfo &&
    109                         ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
    110                         ((LauncherAppWidgetInfo) info).providerName.getClassName()
    111                                 .equals(AppWidgetNoConfig.class.getName());
    112             }
    113         }, command);
    114     }
    115 
    116     public void testPinWidgetWithConfig() throws Throwable {
    117         runTest("pinWidgetWithConfig", true, new ItemOperator() {
    118             @Override
    119             public boolean evaluate(ItemInfo info, View view) {
    120                 return info instanceof LauncherAppWidgetInfo &&
    121                         ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
    122                         ((LauncherAppWidgetInfo) info).providerName.getClassName()
    123                                 .equals(AppWidgetWithConfig.class.getName());
    124             }
    125         });
    126     }
    127 
    128     public void testPinShortcut() throws Throwable {
    129         // Command to set the shortcut id
    130         Intent command = RequestPinItemActivity.getCommandIntent(
    131                 RequestPinItemActivity.class, "setShortcutId").putExtra(
    132                 RequestPinItemActivity.EXTRA_PARAM + "0", mShortcutId);
    133 
    134         runTest("pinShortcut", false, new ItemOperator() {
    135             @Override
    136             public boolean evaluate(ItemInfo info, View view) {
    137                 return info instanceof ShortcutInfo &&
    138                         info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
    139                         ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId);
    140             }
    141         }, command);
    142     }
    143 
    144     private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
    145             Intent... commandIntents) throws Throwable {
    146         if (!Utilities.isAtLeastO()) {
    147             return;
    148         }
    149         lockRotation(true);
    150 
    151         clearHomescreen();
    152         startLauncher();
    153 
    154         // Open all apps and wait for load complete
    155         final UiObject2 appsContainer = openAllApps();
    156         assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
    157 
    158         // Open Pin item activity
    159         BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
    160                 RequestPinItemActivity.class.getName());
    161         scrollAndFind(appsContainer, By.text("Test Pin Item")).click();
    162         assertNotNull(openMonitor.blockingGetExtraIntent());
    163 
    164         // Set callback
    165         PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0,
    166                 new Intent(mCallbackAction), PendingIntent.FLAG_ONE_SHOT);
    167         mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
    168                 RequestPinItemActivity.class, "setCallback").putExtra(
    169                 RequestPinItemActivity.EXTRA_PARAM + "0", callback));
    170 
    171         for (Intent command : commandIntents) {
    172             mTargetContext.sendBroadcast(command);
    173         }
    174 
    175         // call the requested method to start the flow
    176         mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
    177                 RequestPinItemActivity.class, activityMethod));
    178         UiObject2 widgetCell = mDevice.wait(
    179                 Until.findObject(By.clazz(WidgetCell.class)), DEFAULT_ACTIVITY_TIMEOUT);
    180         assertNotNull(widgetCell);
    181 
    182         // Accept confirmation:
    183         BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction);
    184         mDevice.wait(Until.findObject(By.text(mTargetContext.getString(
    185                 R.string.place_automatically).toUpperCase())), DEFAULT_UI_TIMEOUT).click();
    186         Intent result = resultReceiver.blockingGetIntent();
    187         assertNotNull(result);
    188         mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
    189         if (isWidget) {
    190             assertNotSame(-1, mAppWidgetId);
    191         }
    192 
    193         // Go back to home
    194         mTargetContext.startActivity(getHomeIntent());
    195         assertTrue(Wait.atMost(new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT));
    196     }
    197 
    198     /**
    199      * Condition for for an item
    200      */
    201     private class ItemSearchCondition extends Condition implements Callable<Boolean> {
    202 
    203         private final ItemOperator mOp;
    204 
    205         ItemSearchCondition(ItemOperator op) {
    206             mOp = op;
    207         }
    208 
    209         @Override
    210         public boolean isTrue() throws Throwable {
    211             return mMainThreadExecutor.submit(this).get();
    212         }
    213 
    214         @Override
    215         public Boolean call() throws Exception {
    216             // Find the resumed launcher
    217             Launcher launcher = null;
    218             for (Activity a : mActivityMonitor.resumed) {
    219                 if (a instanceof Launcher) {
    220                     launcher = (Launcher) a;
    221                 }
    222             }
    223             if (launcher == null) {
    224                 return false;
    225             }
    226             return launcher.getWorkspace().getFirstMatch(mOp) != null;
    227         }
    228     }
    229 }
    230