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 */
16package com.android.launcher3.ui.widget;
17
18import android.appwidget.AppWidgetManager;
19import android.content.Intent;
20import android.support.test.filters.LargeTest;
21import android.support.test.runner.AndroidJUnit4;
22import android.support.test.uiautomator.By;
23import android.support.test.uiautomator.UiObject2;
24import android.view.View;
25
26import com.android.launcher3.ItemInfo;
27import com.android.launcher3.LauncherAppWidgetInfo;
28import com.android.launcher3.LauncherAppWidgetProviderInfo;
29import com.android.launcher3.Workspace;
30import com.android.launcher3.testcomponent.WidgetConfigActivity;
31import com.android.launcher3.ui.AbstractLauncherUiTest;
32import com.android.launcher3.util.Condition;
33import com.android.launcher3.util.Wait;
34import com.android.launcher3.util.rule.LauncherActivityRule;
35import com.android.launcher3.util.rule.ShellCommandRule;
36import com.android.launcher3.widget.WidgetCell;
37
38import org.junit.Before;
39import org.junit.Rule;
40import org.junit.Test;
41import org.junit.runner.RunWith;
42
43import static org.junit.Assert.assertEquals;
44import static org.junit.Assert.assertNotNull;
45import static org.junit.Assert.assertNotSame;
46import static org.junit.Assert.assertTrue;
47
48/**
49 * Test to verify widget configuration is properly shown.
50 */
51@LargeTest
52@RunWith(AndroidJUnit4.class)
53public class AddConfigWidgetTest extends AbstractLauncherUiTest {
54
55    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
56    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind();
57
58    private LauncherAppWidgetProviderInfo mWidgetInfo;
59    private AppWidgetManager mAppWidgetManager;
60
61    private int mWidgetId;
62
63    @Override
64    @Before
65    public void setUp() throws Exception {
66        super.setUp();
67        mWidgetInfo = findWidgetProvider(true /* hasConfigureScreen */);
68        mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext);
69    }
70
71    @Test
72    public void testWidgetConfig() throws Throwable {
73        runTest(false, true);
74    }
75
76    @Test
77    public void testWidgetConfig_rotate() throws Throwable {
78        runTest(true, true);
79    }
80
81    @Test
82    public void testConfigCancelled() throws Throwable {
83        runTest(false, false);
84    }
85
86    @Test
87    public void testConfigCancelled_rotate() throws Throwable {
88        runTest(true, false);
89    }
90
91    /**
92     * @param rotateConfig should the config screen be rotated
93     * @param acceptConfig accept the config activity
94     */
95    private void runTest(boolean rotateConfig, boolean acceptConfig) throws Throwable {
96        lockRotation(true);
97
98        clearHomescreen();
99        mActivityMonitor.startLauncher();
100
101        // Open widget tray and wait for load complete.
102        final UiObject2 widgetContainer = openWidgetsTray();
103        assertTrue(Wait.atMost(Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT));
104
105        // Drag widget to homescreen
106        WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
107        UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
108                .hasDescendant(By.text(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))));
109        dragToWorkspace(widget, false);
110        // Widget id for which the config activity was opened
111        mWidgetId = monitor.getWidgetId();
112
113        if (rotateConfig) {
114            // Rotate the screen and verify that the config activity is recreated
115            monitor = new WidgetConfigStartupMonitor();
116            lockRotation(false);
117            assertEquals(mWidgetId, monitor.getWidgetId());
118        }
119
120        // Verify that the widget id is valid and bound
121        assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
122
123        setResult(acceptConfig);
124        if (acceptConfig) {
125            assertTrue(Wait.atMost(new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT));
126            assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
127        } else {
128            // Verify that the widget id is deleted.
129            assertTrue(Wait.atMost(new Condition() {
130                @Override
131                public boolean isTrue() throws Throwable {
132                    return mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null;
133                }
134            }, DEFAULT_ACTIVITY_TIMEOUT));
135        }
136    }
137
138    private void setResult(boolean success) {
139
140        getInstrumentation().getTargetContext().sendBroadcast(
141                WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
142                        success ? "clickOK" : "clickCancel"));
143    }
144
145    /**
146     * Condition for searching widget id
147     */
148    private class WidgetSearchCondition extends Condition
149            implements Workspace.ItemOperator {
150
151        @Override
152        public boolean isTrue() throws Throwable {
153            return mMainThreadExecutor.submit(mActivityMonitor.itemExists(this)).get();
154        }
155
156        @Override
157        public boolean evaluate(ItemInfo info, View view) {
158            return info instanceof LauncherAppWidgetInfo &&
159                    ((LauncherAppWidgetInfo) info).providerName.equals(mWidgetInfo.provider) &&
160                    ((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId;
161        }
162    }
163
164    /**
165     * Broadcast receiver for receiving widget config activity status.
166     */
167    private class WidgetConfigStartupMonitor extends BlockingBroadcastReceiver {
168
169        public WidgetConfigStartupMonitor() {
170            super(WidgetConfigActivity.class.getName());
171        }
172
173        public int getWidgetId() throws InterruptedException {
174            Intent intent = blockingGetExtraIntent();
175            assertNotNull(intent);
176            assertEquals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, intent.getAction());
177            int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
178                    LauncherAppWidgetInfo.NO_ID);
179            assertNotSame(widgetId, LauncherAppWidgetInfo.NO_ID);
180            return widgetId;
181        }
182    }
183}
184