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, mLauncher.getRightGestureMarginInContainer(widgetsContainer) + 1, 81 0), 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 targetAppSelector = By.clazz("android.widget.TextView").text( 172 mLauncher.getContext().getPackageName()); 173 final BySelector widgetsContainerSelector = By.res(mLauncher.getLauncherPackageName(), 174 "widgets_table"); 175 176 boolean hasHeaderExpanded = false; 177 int scrollDistance = 0; 178 for (int i = 0; i < SCROLL_ATTEMPTS; i++) { 179 UiObject2 fullWidgetsPicker = verifyActiveContainer(); 180 181 UiObject2 header = mLauncher.waitForObjectInContainer(fullWidgetsPicker, 182 headerSelector); 183 // If a header is barely visible in the bottom edge of the screen, its height could be 184 // too small for a scroll gesture. Since all header should have roughly the same height, 185 // let's pick the max height we have seen so far. 186 scrollDistance = Math.max(scrollDistance, header.getVisibleBounds().height()); 187 188 // Look for a header that has the test app name. 189 UiObject2 headerTitle = mLauncher.findObjectInContainer(fullWidgetsPicker, 190 targetAppSelector); 191 if (headerTitle != null) { 192 // If we find the header and it has not been expanded, let's click it to see the 193 // widgets list. Note that we wait until the header is out of the gesture region at 194 // the bottom of the screen, because tapping there in Launcher3 causes NexusLauncher 195 // to briefly appear to handle the gesture, which can break our test. 196 boolean isHeaderOutOfGestureRegion = headerTitle.getVisibleCenter().y 197 < mLauncher.getBottomGestureStartOnScreen(); 198 if (!hasHeaderExpanded && isHeaderOutOfGestureRegion) { 199 log("Header has not been expanded. Click to expand."); 200 hasHeaderExpanded = true; 201 mLauncher.clickLauncherObject(headerTitle); 202 } 203 204 // Look for a widgets list. 205 UiObject2 widgetsContainer = mLauncher.findObjectInContainer(fullWidgetsPicker, 206 widgetsContainerSelector); 207 if (widgetsContainer != null) { 208 log("Widgets container found."); 209 return widgetsContainer; 210 } 211 } 212 log("Finding test widget package - scroll with distance: " + scrollDistance); 213 mLauncher.scrollDownByDistance(fullWidgetsPicker, scrollDistance); 214 } 215 216 return null; 217 } 218 } 219