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
setUp()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
tearDown()81     protected void tearDown() throws Exception {
82         ((Application) getInstrumentation().getTargetContext().getApplicationContext())
83                 .unregisterActivityLifecycleCallbacks(mActivityMonitor);
84         super.tearDown();
85     }
86 
testPinWidgetNoConfig()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 
testPinWidgetNoConfig_customPreview()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 
testPinWidgetWithConfig()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 
testPinShortcut()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 
runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher, Intent... commandIntents)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 
ItemSearchCondition(ItemOperator op)205         ItemSearchCondition(ItemOperator op) {
206             mOp = op;
207         }
208 
209         @Override
isTrue()210         public boolean isTrue() throws Throwable {
211             return mMainThreadExecutor.submit(this).get();
212         }
213 
214         @Override
call()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