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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
19 
20 import static com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertNotSame;
25 
26 import android.appwidget.AppWidgetManager;
27 import android.content.Intent;
28 import android.view.View;
29 
30 import androidx.test.filters.LargeTest;
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import com.android.launcher3.Launcher;
34 import com.android.launcher3.celllayout.FavoriteItemsTransaction;
35 import com.android.launcher3.model.data.ItemInfo;
36 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
37 import com.android.launcher3.testcomponent.WidgetConfigActivity;
38 import com.android.launcher3.ui.AbstractLauncherUiTest;
39 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
40 import com.android.launcher3.ui.TestViewHelpers;
41 import com.android.launcher3.util.Wait;
42 import com.android.launcher3.util.rule.ShellCommandRule;
43 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
44 
45 import org.junit.Before;
46 import org.junit.Rule;
47 import org.junit.Test;
48 import org.junit.runner.RunWith;
49 
50 /**
51  * Test to verify widget configuration is properly shown.
52  */
53 @LargeTest
54 @RunWith(AndroidJUnit4.class)
55 public class TaplAddConfigWidgetTest extends AbstractLauncherUiTest<Launcher> {
56 
57     @Rule
58     public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
59 
60     private LauncherAppWidgetProviderInfo mWidgetInfo;
61     private AppWidgetManager mAppWidgetManager;
62 
63     private int mWidgetId;
64 
65     @Override
66     @Before
setUp()67     public void setUp() throws Exception {
68         super.setUp();
69         mWidgetInfo = TestViewHelpers.findWidgetProvider(true /* hasConfigureScreen */);
70         mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext);
71     }
72 
73     @Test
74     @PortraitLandscape
testWidgetConfig()75     public void testWidgetConfig() throws Throwable {
76         runTest(true);
77     }
78 
79     @Test
80     @PortraitLandscape
testConfigCancelled()81     public void testConfigCancelled() throws Throwable {
82         runTest(false);
83     }
84 
85 
86     /**
87      * @param acceptConfig accept the config activity
88      */
runTest(boolean acceptConfig)89     private void runTest(boolean acceptConfig) throws Throwable {
90         commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
91 
92         // Drag widget to homescreen
93         WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
94         mLauncher.getWorkspace()
95                 .openAllWidgets()
96                 .getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))
97                 .dragToWorkspace(true, false);
98         // Widget id for which the config activity was opened
99         mWidgetId = monitor.getWidgetId();
100 
101         // Verify that the widget id is valid and bound
102         assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
103 
104         setResultAndWaitForAnimation(acceptConfig);
105         if (acceptConfig) {
106             Wait.atMost("", new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
107             assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
108         } else {
109             // Verify that the widget id is deleted.
110             Wait.atMost("", () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
111                     DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
112         }
113     }
114 
setResult(boolean success)115     private static void setResult(boolean success) {
116         getInstrumentation().getTargetContext().sendBroadcast(
117                 WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
118                         success ? "clickOK" : "clickCancel"));
119     }
120 
setResultAndWaitForAnimation(boolean success)121     private void setResultAndWaitForAnimation(boolean success) {
122         if (mLauncher.isLauncher3()) {
123             setResult(success);
124         } else {
125             mLauncher.executeAndWaitForWallpaperAnimation(
126                     () -> setResult(success),
127                     "setting widget coinfig result");
128         }
129     }
130 
131     /**
132      * Condition for searching widget id
133      */
134     private class WidgetSearchCondition implements Wait.Condition, ItemOperator {
135 
136         @Override
isTrue()137         public boolean isTrue() throws Throwable {
138             return mMainThreadExecutor.submit(() -> {
139                 Launcher l = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
140                 return l != null && l.getWorkspace().getFirstMatch(this) != null;
141             }).get();
142         }
143 
144         @Override
evaluate(ItemInfo info, View view)145         public boolean evaluate(ItemInfo info, View view) {
146             return info instanceof LauncherAppWidgetInfo
147                     && ((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
148                             mWidgetInfo.provider.getClassName())
149                     && ((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId;
150         }
151     }
152 
153     /**
154      * Broadcast receiver for receiving widget config activity status.
155      */
156     private class WidgetConfigStartupMonitor extends BlockingBroadcastReceiver {
157 
WidgetConfigStartupMonitor()158         public WidgetConfigStartupMonitor() {
159             super(WidgetConfigActivity.class.getName());
160         }
161 
getWidgetId()162         public int getWidgetId() throws InterruptedException {
163             Intent intent = blockingGetExtraIntent();
164             assertNotNull("Null EXTRA_INTENT", intent);
165             assertEquals("Intent action is not ACTION_APPWIDGET_CONFIGURE",
166                     AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, intent.getAction());
167             int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
168                     LauncherAppWidgetInfo.NO_ID);
169             assertNotSame("Widget id is NO_ID", widgetId, LauncherAppWidgetInfo.NO_ID);
170             return widgetId;
171         }
172     }
173 }
174