1 /* 2 * Copyright (C) 2020 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 android.autofillservice.cts.testcore; 18 19 import static android.autofillservice.cts.testcore.Timeouts.DATASET_PICKER_NOT_SHOWN_NAPTIME_MS; 20 import static android.autofillservice.cts.testcore.Timeouts.LONG_PRESS_MS; 21 import static android.autofillservice.cts.testcore.Timeouts.UI_TIMEOUT; 22 23 import android.content.Context; 24 import android.content.pm.PackageManager; 25 import android.content.res.Resources; 26 import android.os.SystemClock; 27 import android.util.Log; 28 29 import androidx.test.uiautomator.By; 30 import androidx.test.uiautomator.BySelector; 31 import androidx.test.uiautomator.Direction; 32 import androidx.test.uiautomator.StaleObjectException; 33 import androidx.test.uiautomator.UiObject2; 34 35 import com.android.compatibility.common.util.RequiredFeatureRule; 36 import com.android.cts.mockime.MockIme; 37 38 import org.junit.rules.RuleChain; 39 import org.junit.rules.TestRule; 40 41 /** 42 * UiBot for the inline suggestion. 43 */ 44 public final class InlineUiBot extends UiBot { 45 46 private static final String TAG = "AutoFillInlineCtsUiBot"; 47 public static final String SUGGESTION_STRIP_DESC = "MockIme Inline Suggestion View"; 48 49 private static final BySelector SUGGESTION_STRIP_SELECTOR = By.desc(SUGGESTION_STRIP_DESC); 50 51 private static final RequiredFeatureRule REQUIRES_IME_RULE = new RequiredFeatureRule( 52 PackageManager.FEATURE_INPUT_METHODS); 53 54 private final Context mContext; 55 InlineUiBot(Context context)56 public InlineUiBot(Context context) { 57 super(UI_TIMEOUT); 58 mContext = context; 59 } 60 annotateRule(TestRule rule)61 public static RuleChain annotateRule(TestRule rule) { 62 return RuleChain.outerRule(REQUIRES_IME_RULE).around(rule); 63 } 64 65 @Override assertNoDatasets()66 public void assertNoDatasets() throws Exception { 67 assertNoDatasetsEver(); 68 } 69 70 @Override assertNoDatasetsEver()71 public void assertNoDatasetsEver() throws Exception { 72 assertNeverShown("suggestion strip", SUGGESTION_STRIP_SELECTOR, 73 DATASET_PICKER_NOT_SHOWN_NAPTIME_MS); 74 } 75 76 /** 77 * Selects the suggestion in the {@link MockIme}'s suggestion strip by the given text. 78 */ selectSuggestion(String name)79 public void selectSuggestion(String name) throws Exception { 80 final UiObject2 strip = findSuggestionStrip(); 81 final UiObject2 dataset = strip.findObject(By.text(name)); 82 if (dataset == null) { 83 throw new AssertionError("no dataset " + name + " in " + getChildrenAsText(strip)); 84 } 85 dataset.click(); 86 } 87 88 @Override selectDataset(String name)89 public void selectDataset(String name) throws Exception { 90 int retryCount = 0; 91 while (retryCount < MAX_UIOBJECT_RETRY_COUNT) { 92 try { 93 selectSuggestion(name); 94 break; 95 } catch (AssertionError ex) { 96 retryCount++; 97 if (retryCount >= MAX_UIOBJECT_RETRY_COUNT) { 98 throw ex; 99 } else { 100 SystemClock.sleep(100); 101 } 102 } 103 } 104 } 105 106 @Override longPressSuggestion(String name)107 public void longPressSuggestion(String name) throws Exception { 108 final UiObject2 strip = findSuggestionStrip(); 109 final UiObject2 dataset = strip.findObject(By.text(name)); 110 if (dataset == null) { 111 throw new AssertionError("no dataset " + name + " in " + getChildrenAsText(strip)); 112 } 113 dataset.click(LONG_PRESS_MS); 114 } 115 116 @Override assertDatasets(String... names)117 public UiObject2 assertDatasets(String... names) throws Exception { 118 UiObject2 picker = null; 119 int retryCount = 0; 120 while (retryCount < MAX_UIOBJECT_RETRY_COUNT) { 121 try { 122 picker = findSuggestionStrip(); 123 return assertDatasets(picker, names); 124 } catch (StaleObjectException ex) { 125 retryCount++; 126 if (retryCount >= MAX_UIOBJECT_RETRY_COUNT) { 127 throw ex; 128 } else { 129 SystemClock.sleep(100); 130 } 131 } 132 } 133 134 // Unreachable, either: 135 // 1. returns assertDatasets(picker, names) 136 // 2. StaleObjectException is thrown above 137 return picker; 138 } 139 140 @Override assertSuggestion(String name)141 public void assertSuggestion(String name) throws Exception { 142 final UiObject2 strip = findSuggestionStrip(); 143 final UiObject2 dataset = strip.findObject(By.text(name)); 144 if (dataset == null) { 145 throw new AssertionError("no dataset " + name + " in " + getChildrenAsText(strip)); 146 } 147 } 148 149 @Override assertNoSuggestion(String name)150 public void assertNoSuggestion(String name) throws Exception { 151 final UiObject2 strip = findSuggestionStrip(); 152 final UiObject2 dataset = strip.findObject(By.text(name)); 153 if (dataset != null) { 154 throw new AssertionError("has dataset " + name + " in " + getChildrenAsText(strip)); 155 } 156 } 157 158 @Override scrollSuggestionView(Direction direction, int speed)159 public void scrollSuggestionView(Direction direction, int speed) throws Exception { 160 final UiObject2 strip = findSuggestionStrip(); 161 final int defaultWidth = strip.getVisibleBounds().width() / 4; 162 final int width = getEdgeSensitivityWidth(defaultWidth); 163 strip.setGestureMargin(width); 164 strip.fling(direction, speed); 165 } 166 assertTooltipShowing(String text)167 public void assertTooltipShowing(String text) throws Exception { 168 final UiObject2 strip = waitForObject(By.text(text), UI_TIMEOUT); 169 if (strip == null) { 170 throw new AssertionError("not find inline tooltip by text: " + text); 171 } 172 } 173 findSuggestionStrip()174 private UiObject2 findSuggestionStrip() throws Exception { 175 return waitForObject(SUGGESTION_STRIP_SELECTOR, Timeouts.UI_TIMEOUT); 176 } 177 getEdgeSensitivityWidth(int defaultWidth)178 private int getEdgeSensitivityWidth(int defaultWidth) { 179 Resources resources = mContext.getResources(); 180 int resId = resources.getIdentifier("config_backGestureInset", "dimen", "android"); 181 try { 182 int width = resources.getDimensionPixelSize(resId) + 1; 183 if (width >= defaultWidth) { 184 return width; 185 } 186 Log.i(TAG, "Got edge sensitivity width of " + width 187 + ", which is less than the default of " + defaultWidth + ". Using default"); 188 return defaultWidth; 189 } catch (Resources.NotFoundException e) { 190 Log.e(TAG, "Failed to get edge sensitivity width. Defaulting to " + defaultWidth, e); 191 return defaultWidth; 192 } 193 } 194 } 195