1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.tapl; 18 19 import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS; 20 import static com.android.launcher3.tapl.LauncherInstrumentation.log; 21 22 import android.graphics.Rect; 23 24 import androidx.test.uiautomator.By; 25 import androidx.test.uiautomator.BySelector; 26 import androidx.test.uiautomator.Direction; 27 import androidx.test.uiautomator.UiObject2; 28 import androidx.test.uiautomator.Until; 29 30 import com.android.launcher3.testing.shared.TestProtocol; 31 32 import java.util.Collection; 33 34 /** 35 * All widgets container. 36 */ 37 public final class Widgets extends LauncherInstrumentation.VisibleContainer { 38 private static final int FLING_STEPS = 10; 39 private static final int SCROLL_ATTEMPTS = 60; 40 Widgets(LauncherInstrumentation launcher)41 Widgets(LauncherInstrumentation launcher) { 42 super(launcher); 43 verifyActiveContainer(); 44 } 45 46 /** 47 * Flings forward (down) and waits the fling's end. 48 */ flingForward()49 public void flingForward() { 50 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 51 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 52 "want to fling forward in widgets")) { 53 log("Widgets.flingForward enter"); 54 final UiObject2 widgetsContainer = verifyActiveContainer(); 55 mLauncher.scroll( 56 widgetsContainer, 57 Direction.DOWN, 58 new Rect(0, 0, 0, 59 mLauncher.getBottomGestureMarginInContainer(widgetsContainer) + 1), 60 FLING_STEPS, false); 61 try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) { 62 verifyActiveContainer(); 63 } 64 log("Widgets.flingForward exit"); 65 } 66 } 67 68 /** 69 * Flings backward (up) and waits the fling's end. 70 */ flingBackward()71 public void flingBackward() { 72 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 73 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 74 "want to fling backwards in widgets")) { 75 log("Widgets.flingBackward enter"); 76 final UiObject2 widgetsContainer = verifyActiveContainer(); 77 mLauncher.scroll( 78 widgetsContainer, 79 Direction.UP, 80 new Rect(0, 0, 0, 81 mLauncher.getBottomGestureMarginInContainer(widgetsContainer) + 1), 82 FLING_STEPS, false); 83 try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) { 84 verifyActiveContainer(); 85 } 86 log("Widgets.flingBackward exit"); 87 } 88 } 89 90 @Override getContainerType()91 protected LauncherInstrumentation.ContainerType getContainerType() { 92 return LauncherInstrumentation.ContainerType.WIDGETS; 93 } 94 getWidgetsScroll()95 private int getWidgetsScroll() { 96 return mLauncher.getTestInfo( 97 TestProtocol.REQUEST_WIDGETS_SCROLL_Y) 98 .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); 99 } 100 getWidget(String labelText)101 public Widget getWidget(String labelText) { 102 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 103 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 104 "getting widget " + labelText + " in widgets list")) { 105 final UiObject2 searchBar = findSearchBar(); 106 final int searchBarHeight = searchBar.getVisibleBounds().height(); 107 final UiObject2 fullWidgetsPicker = verifyActiveContainer(); 108 mLauncher.assertTrue("Widgets container didn't become scrollable", 109 fullWidgetsPicker.wait(Until.scrollable(true), WAIT_TIME_MS)); 110 111 final UiObject2 widgetsContainer = findTestAppWidgetsTableContainer(); 112 mLauncher.assertTrue("Can't locate widgets list for the test app: " 113 + mLauncher.getLauncherPackageName(), 114 widgetsContainer != null); 115 final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText); 116 final BySelector previewSelector = By.res(mLauncher.getLauncherPackageName(), 117 "widget_preview"); 118 final int bottomGestureStartOnScreen = mLauncher.getBottomGestureStartOnScreen(); 119 int i = 0; 120 for (; ; ) { 121 final Collection<UiObject2> tableRows = mLauncher.getChildren(widgetsContainer); 122 for (UiObject2 row : tableRows) { 123 final Collection<UiObject2> widgetCells = mLauncher.getChildren(row); 124 for (UiObject2 widget : widgetCells) { 125 final UiObject2 label = mLauncher.findObjectInContainer(widget, 126 labelSelector); 127 if (label == null) { 128 continue; 129 } 130 if (widget.getVisibleCenter().y >= bottomGestureStartOnScreen) { 131 continue; 132 } 133 mLauncher.assertEquals( 134 "View is not WidgetCell", 135 "com.android.launcher3.widget.WidgetCell", 136 widget.getClassName()); 137 UiObject2 preview = mLauncher.waitForObjectInContainer(widget, 138 previewSelector); 139 return new Widget(mLauncher, preview); 140 } 141 } 142 143 mLauncher.assertTrue("Too many attempts", ++i <= SCROLL_ATTEMPTS); 144 final int scroll = getWidgetsScroll(); 145 mLauncher.scrollDownByDistance(fullWidgetsPicker, searchBarHeight); 146 final int newScroll = getWidgetsScroll(); 147 mLauncher.assertTrue( 148 "Scrolled in a wrong direction in Widgets: from " + scroll + " to " 149 + newScroll, newScroll >= scroll); 150 mLauncher.assertTrue("Unable to scroll to the widget", newScroll != scroll); 151 } 152 } 153 } 154 findSearchBar()155 private UiObject2 findSearchBar() { 156 final BySelector searchBarContainerSelector = By.res(mLauncher.getLauncherPackageName(), 157 "search_and_recommendations_container"); 158 final BySelector searchBarSelector = By.res(mLauncher.getLauncherPackageName(), 159 "widgets_search_bar"); 160 final UiObject2 searchBarContainer = mLauncher.waitForLauncherObject( 161 searchBarContainerSelector); 162 UiObject2 searchBar = mLauncher.waitForObjectInContainer(searchBarContainer, 163 searchBarSelector); 164 return searchBar; 165 } 166 167 /** Finds the widgets list of this test app from the collapsed full widgets picker. */ findTestAppWidgetsTableContainer()168 private UiObject2 findTestAppWidgetsTableContainer() { 169 final BySelector headerSelector = By.res(mLauncher.getLauncherPackageName(), 170 "widgets_list_header"); 171 final BySelector widgetPickerSelector = By.res(mLauncher.getLauncherPackageName(), 172 "container"); 173 final BySelector targetAppSelector = By.clazz("android.widget.TextView").text( 174 mLauncher.getContext().getPackageName()); 175 final BySelector widgetsContainerSelector = By.res(mLauncher.getLauncherPackageName(), 176 "widgets_table"); 177 178 boolean hasHeaderExpanded = false; 179 int scrollDistance = 0; 180 for (int i = 0; i < SCROLL_ATTEMPTS; i++) { 181 UiObject2 widgetPicker = mLauncher.waitForLauncherObject(widgetPickerSelector); 182 UiObject2 widgetListView = verifyActiveContainer(); 183 UiObject2 header = mLauncher.waitForObjectInContainer(widgetListView, 184 headerSelector); 185 // If a header is barely visible in the bottom edge of the screen, its height could be 186 // too small for a scroll gesture. Since all header should have roughly the same height, 187 // let's pick the max height we have seen so far. 188 scrollDistance = Math.max(scrollDistance, header.getVisibleBounds().height()); 189 190 // Look for a header that has the test app name. 191 UiObject2 headerTitle = mLauncher.findObjectInContainer(widgetListView, 192 targetAppSelector); 193 if (headerTitle != null) { 194 // If we find the header and it has not been expanded, let's click it to see the 195 // widgets list. Note that we wait until the header is out of the gesture region at 196 // the bottom of the screen, because tapping there in Launcher3 causes NexusLauncher 197 // to briefly appear to handle the gesture, which can break our test. 198 boolean isHeaderOutOfGestureRegion = headerTitle.getVisibleCenter().y 199 < mLauncher.getBottomGestureStartOnScreen(); 200 if (!hasHeaderExpanded && isHeaderOutOfGestureRegion) { 201 log("Header has not been expanded. Click to expand."); 202 hasHeaderExpanded = true; 203 mLauncher.clickLauncherObject(headerTitle); 204 } 205 206 // If we are in a tablet in landscape mode then we will have a two pane view and we 207 // use the right pane to display the widgets table. 208 UiObject2 rightPane = mLauncher.findObjectInContainer( 209 widgetPicker, 210 widgetsContainerSelector); 211 212 // Look for a widgets list. 213 UiObject2 widgetsContainer = mLauncher.findObjectInContainer( 214 rightPane != null ? rightPane : widgetListView, 215 widgetsContainerSelector); 216 if (widgetsContainer != null) { 217 log("Widgets container found."); 218 return widgetsContainer; 219 } 220 } 221 log("Finding test widget package - scroll with distance: " + scrollDistance); 222 223 // If we are in a tablet in landscape mode then we will have a two pane view and we use 224 // the right pane to display the widgets table. 225 UiObject2 rightPane = mLauncher.findObjectInContainer( 226 widgetPicker, 227 widgetsContainerSelector); 228 229 mLauncher.scrollDownByDistance(hasHeaderExpanded && rightPane != null 230 ? rightPane 231 : widgetListView, scrollDistance); 232 } 233 234 return null; 235 } 236 } 237