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 static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
19 
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertNotSame;
22 
23 import android.app.PendingIntent;
24 import android.appwidget.AppWidgetManager;
25 import android.content.Intent;
26 import android.graphics.Color;
27 import android.view.View;
28 
29 import androidx.test.filters.LargeTest;
30 import androidx.test.runner.AndroidJUnit4;
31 
32 import com.android.launcher3.LauncherSettings.Favorites;
33 import com.android.launcher3.model.data.ItemInfo;
34 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
35 import com.android.launcher3.model.data.WorkspaceItemInfo;
36 import com.android.launcher3.shortcuts.ShortcutKey;
37 import com.android.launcher3.tapl.AddToHomeScreenPrompt;
38 import com.android.launcher3.testcomponent.AppWidgetNoConfig;
39 import com.android.launcher3.testcomponent.AppWidgetWithConfig;
40 import com.android.launcher3.testcomponent.RequestPinItemActivity;
41 import com.android.launcher3.ui.AbstractLauncherUiTest;
42 import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
43 import com.android.launcher3.util.Wait;
44 import com.android.launcher3.util.Wait.Condition;
45 import com.android.launcher3.util.rule.ShellCommandRule;
46 
47 import org.junit.Before;
48 import org.junit.Rule;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 
52 import java.util.UUID;
53 
54 /**
55  * Test to verify pin item request flow.
56  */
57 @LargeTest
58 @RunWith(AndroidJUnit4.class)
59 public class RequestPinItemTest extends AbstractLauncherUiTest {
60 
61     @Rule
62     public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
63 
64     private String mCallbackAction;
65     private String mShortcutId;
66     private int mAppWidgetId;
67 
68     @Override
69     @Before
setUp()70     public void setUp() throws Exception {
71         super.setUp();
72         mCallbackAction = UUID.randomUUID().toString();
73         mShortcutId = UUID.randomUUID().toString();
74     }
75 
76     @Test
testEmpty()77     public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ }
78 
79     @Test
testPinWidgetNoConfig()80     public void testPinWidgetNoConfig() throws Throwable {
81         runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
82                 ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
83                 ((LauncherAppWidgetInfo) info).providerName.getClassName()
84                         .equals(AppWidgetNoConfig.class.getName()));
85     }
86 
87     @Test
testPinWidgetNoConfig_customPreview()88     public void testPinWidgetNoConfig_customPreview() throws Throwable {
89         // Command to set custom preview
90         Intent command = RequestPinItemActivity.getCommandIntent(
91                 RequestPinItemActivity.class, "setRemoteViewColor").putExtra(
92                 RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED);
93 
94         runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
95                 ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
96                 ((LauncherAppWidgetInfo) info).providerName.getClassName()
97                         .equals(AppWidgetNoConfig.class.getName()), command);
98     }
99 
100     @Test
testPinWidgetWithConfig()101     public void testPinWidgetWithConfig() throws Throwable {
102         runTest("pinWidgetWithConfig", true,
103                 (info, view) -> info instanceof LauncherAppWidgetInfo &&
104                         ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
105                         ((LauncherAppWidgetInfo) info).providerName.getClassName()
106                                 .equals(AppWidgetWithConfig.class.getName()));
107     }
108 
109     @Test
testPinShortcut()110     public void testPinShortcut() throws Throwable {
111         // Command to set the shortcut id
112         Intent command = RequestPinItemActivity.getCommandIntent(
113                 RequestPinItemActivity.class, "setShortcutId").putExtra(
114                 RequestPinItemActivity.EXTRA_PARAM + "0", mShortcutId);
115 
116         runTest("pinShortcut", false, new ItemOperator() {
117             @Override
118             public boolean evaluate(ItemInfo info, View view) {
119                 return info instanceof WorkspaceItemInfo &&
120                         info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
121                         ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId);
122             }
123         }, command);
124     }
125 
runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher, Intent... commandIntents)126     private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
127             Intent... commandIntents) throws Throwable {
128         clearHomescreen();
129         mDevice.pressHome();
130 
131         // Open Pin item activity
132         BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
133                 RequestPinItemActivity.class.getName());
134         mLauncher.
135                 getWorkspace().
136                 switchToAllApps().
137                 getAppIcon("Test Pin Item").
138                 launch(getAppPackageName());
139         assertNotNull(openMonitor.blockingGetExtraIntent());
140 
141         // Set callback
142         PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0,
143                 new Intent(mCallbackAction), PendingIntent.FLAG_ONE_SHOT);
144         mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
145                 RequestPinItemActivity.class, "setCallback").putExtra(
146                 RequestPinItemActivity.EXTRA_PARAM + "0", callback));
147 
148         for (Intent command : commandIntents) {
149             mTargetContext.sendBroadcast(command);
150         }
151 
152         // call the requested method to start the flow
153         mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
154                 RequestPinItemActivity.class, activityMethod));
155         final AddToHomeScreenPrompt addToHomeScreenPrompt = mLauncher.getAddToHomeScreenPrompt();
156 
157         // Accept confirmation:
158         BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction);
159         addToHomeScreenPrompt.addAutomatically();
160         Intent result = resultReceiver.blockingGetIntent();
161         assertNotNull(result);
162         mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
163         if (isWidget) {
164             assertNotSame(-1, mAppWidgetId);
165         }
166 
167         // Go back to home
168         mLauncher.pressHome();
169         Wait.atMost("", new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT,
170                 mLauncher);
171     }
172 
173     /**
174      * Condition for for an item
175      */
176     private class ItemSearchCondition implements Condition {
177 
178         private final ItemOperator mOp;
179 
ItemSearchCondition(ItemOperator op)180         ItemSearchCondition(ItemOperator op) {
181             mOp = op;
182         }
183 
184         @Override
isTrue()185         public boolean isTrue() throws Throwable {
186             return mMainThreadExecutor.submit(mActivityMonitor.itemExists(mOp)).get();
187         }
188     }
189 }
190