1 /* 2 * Copyright (C) 2017 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 package android.autofillservice.cts.commontests; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import static org.junit.Assume.assumeTrue; 21 22 import android.app.Activity; 23 import android.app.PendingIntent; 24 import android.autofillservice.cts.R; 25 import android.autofillservice.cts.activities.AbstractAutoFillActivity; 26 import android.autofillservice.cts.testcore.Helper; 27 import android.autofillservice.cts.testcore.UiBot; 28 import android.content.Intent; 29 import android.service.autofill.CustomDescription; 30 import android.widget.RemoteViews; 31 32 import androidx.annotation.NonNull; 33 import androidx.test.filters.FlakyTest; 34 import androidx.test.uiautomator.By; 35 import androidx.test.uiautomator.UiObject2; 36 37 import org.junit.Test; 38 39 /** 40 * Template for tests cases that test what happens when a link in the {@link CustomDescription} is 41 * tapped by the user. 42 * 43 * <p>It must be extend by 2 sub-class to provide tests for the 2 distinct scenarios: 44 * <ul> 45 * <li>Save is triggered by 1st activity finishing and launching a 2nd activity. 46 * <li>Save is triggered by explicit {@link android.view.autofill.AutofillManager#commit()} call 47 * and shown in the same activity. 48 * </ul> 49 * 50 * <p>The overall behavior should be the same in both cases, although the implementation of the 51 * tests per se will be sligthly different. 52 */ 53 public abstract class CustomDescriptionWithLinkTestCase<A extends AbstractAutoFillActivity> extends 54 AutoFillServiceTestCase.AutoActivityLaunch<A> { 55 56 private static final String ID_LINK = "link"; 57 58 private final Class<A> mActivityClass; 59 60 protected A mActivity; 61 CustomDescriptionWithLinkTestCase(@onNull Class<A> activityClass)62 protected CustomDescriptionWithLinkTestCase(@NonNull Class<A> activityClass) { 63 mActivityClass = activityClass; 64 } 65 startActivity()66 protected void startActivity() { 67 startActivity(false); 68 } 69 startActivity(boolean remainOnRecents)70 protected void startActivity(boolean remainOnRecents) { 71 final Intent intent = new Intent(mContext, mActivityClass); 72 if (remainOnRecents) { 73 intent.setFlags( 74 Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_TASK); 75 } 76 mActivity = launchActivity(intent); 77 } 78 79 /** 80 * Tests scenarios when user taps a link in the custom description and then taps back: 81 * the Save UI should have been restored. 82 */ 83 @Test testTapLink_tapBack()84 public final void testTapLink_tapBack() throws Exception { 85 saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction.TAP_BACK_BUTTON); 86 } 87 88 /** 89 * Tests scenarios when user taps a link in the custom description, change the screen 90 * orientation while the new activity is show, then taps back: 91 * the Save UI should have been restored. 92 */ 93 @Test testTapLink_changeOrientationThenTapBack()94 public final void testTapLink_changeOrientationThenTapBack() throws Exception { 95 assumeTrue("Rotation is supported", Helper.isRotationSupported(mContext)); 96 assumeTrue("Device state is not REAR_DISPLAY", 97 !Helper.isDeviceInState(mContext, Helper.DeviceStateEnum.REAR_DISPLAY)); 98 99 mUiBot.assumeMinimumResolution(500); 100 mUiBot.setScreenOrientation(UiBot.PORTRAIT); 101 try { 102 saveUiRestoredAfterTappingLinkTest( 103 PostSaveLinkTappedAction.ROTATE_THEN_TAP_BACK_BUTTON); 104 } finally { 105 try { 106 mUiBot.setScreenOrientation(UiBot.PORTRAIT); 107 cleanUpAfterScreenOrientationIsBackToPortrait(); 108 } catch (Exception e) { 109 mSafeCleanerRule.add(e); 110 } finally { 111 mUiBot.resetScreenResolution(); 112 } 113 } 114 } 115 116 /** 117 * Tests scenarios when user taps a link in the custom description, then the new activity 118 * finishes: 119 * the Save UI should have been restored. 120 */ 121 @Test testTapLink_finishActivity()122 public final void testTapLink_finishActivity() throws Exception { 123 saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction.FINISH_ACTIVITY); 124 } 125 saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction type)126 protected abstract void saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction type) 127 throws Exception; 128 cleanUpAfterScreenOrientationIsBackToPortrait()129 protected void cleanUpAfterScreenOrientationIsBackToPortrait() throws Exception { 130 } 131 132 /** 133 * Tests scenarios when user taps a link in the custom description, taps back to return to the 134 * activity with the Save UI, and touch outside the Save UI to dismiss it. 135 * 136 * <p>Then user starts a new session by focusing in a field. 137 */ 138 @Test testTapLink_tapBack_thenStartOverByTouchOutsideAndFocus()139 public final void testTapLink_tapBack_thenStartOverByTouchOutsideAndFocus() 140 throws Exception { 141 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TOUCH_OUTSIDE, false); 142 } 143 144 /** 145 * Tests scenarios when user taps a link in the custom description, taps back to return to the 146 * activity with the Save UI, and touch outside the Save UI to dismiss it. 147 * 148 * <p>Then user starts a new session by forcing autofill. 149 */ 150 @Test testTapLink_tapBack_thenStartOverByTouchOutsideAndManualRequest()151 public void testTapLink_tapBack_thenStartOverByTouchOutsideAndManualRequest() 152 throws Exception { 153 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TOUCH_OUTSIDE, true); 154 } 155 156 /** 157 * Tests scenarios when user taps a link in the custom description, taps back to return to the 158 * activity with the Save UI, and tap the "No" button to dismiss it. 159 * 160 * <p>Then user starts a new session by focusing in a field. 161 */ 162 @Test testTapLink_tapBack_thenStartOverBySayingNoAndFocus()163 public final void testTapLink_tapBack_thenStartOverBySayingNoAndFocus() 164 throws Exception { 165 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_NO_ON_SAVE_UI, 166 false); 167 } 168 169 /** 170 * Tests scenarios when user taps a link in the custom description, taps back to return to the 171 * activity with the Save UI, and tap the "No" button to dismiss it. 172 * 173 * <p>Then user starts a new session by forcing autofill. 174 */ 175 @Test testTapLink_tapBack_thenStartOverBySayingNoAndManualRequest()176 public final void testTapLink_tapBack_thenStartOverBySayingNoAndManualRequest() 177 throws Exception { 178 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_NO_ON_SAVE_UI, true); 179 } 180 181 /** 182 * Tests scenarios when user taps a link in the custom description, taps back to return to the 183 * activity with the Save UI, and the "Yes" button to save it. 184 * 185 * <p>Then user starts a new session by focusing in a field. 186 */ 187 @Test testTapLink_tapBack_thenStartOverBySayingYesAndFocus()188 public final void testTapLink_tapBack_thenStartOverBySayingYesAndFocus() 189 throws Exception { 190 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_YES_ON_SAVE_UI, 191 false); 192 } 193 194 /** 195 * Tests scenarios when user taps a link in the custom description, taps back to return to the 196 * activity with the Save UI, and the "Yes" button to save it. 197 * 198 * <p>Then user starts a new session by forcing autofill. 199 */ 200 @Test testTapLink_tapBack_thenStartOverBySayingYesAndManualRequest()201 public final void testTapLink_tapBack_thenStartOverBySayingYesAndManualRequest() 202 throws Exception { 203 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_YES_ON_SAVE_UI, true); 204 } 205 tapLinkThenTapBackThenStartOverTest( PostSaveLinkTappedAction action, boolean manualRequest)206 protected abstract void tapLinkThenTapBackThenStartOverTest( 207 PostSaveLinkTappedAction action, boolean manualRequest) throws Exception; 208 209 /** 210 * Tests scenarios when user taps a link in the custom description, then re-launches the 211 * original activity: 212 * the Save UI should have been canceled. 213 */ 214 @Test testTapLink_backToPreviousActivityByLaunchingIt()215 public final void testTapLink_backToPreviousActivityByLaunchingIt() 216 throws Exception { 217 saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction.LAUNCH_PREVIOUS_ACTIVITY); 218 } 219 220 /** 221 * Tests scenarios when user taps a link in the custom description, then launches a 3rd 222 * activity: 223 * the Save UI should have been canceled. 224 */ 225 @Test testTapLink_launchNewActivityThenTapBack()226 public final void testTapLink_launchNewActivityThenTapBack() throws Exception { 227 saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction.LAUNCH_NEW_ACTIVITY); 228 } 229 saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction type)230 protected abstract void saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction type) 231 throws Exception; 232 233 @Test 234 @FlakyTest(bugId = 177259617) testTapLink_launchTrampolineActivityThenTapBackAndStartNewSession()235 public final void testTapLink_launchTrampolineActivityThenTapBackAndStartNewSession() 236 throws Exception { 237 // Reset AutofillOptions to avoid cts package was added to augmented autofill allowlist. 238 Helper.resetApplicationAutofillOptions(sContext); 239 240 tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest(); 241 242 // Clear AutofillOptions. 243 Helper.clearApplicationAutofillOptions(sContext); 244 } 245 tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest()246 protected abstract void tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest() 247 throws Exception; 248 249 @Test testTapLinkAfterUpdateAppliedToLinkView()250 public final void testTapLinkAfterUpdateAppliedToLinkView() throws Exception { 251 tapLinkAfterUpdateAppliedTest(true); 252 } 253 254 @Test testTapLinkAfterUpdateAppliedToAnotherView()255 public final void testTapLinkAfterUpdateAppliedToAnotherView() throws Exception { 256 tapLinkAfterUpdateAppliedTest(false); 257 } 258 tapLinkAfterUpdateAppliedTest(boolean updateLinkView)259 protected abstract void tapLinkAfterUpdateAppliedTest(boolean updateLinkView) throws Exception; 260 261 public enum PostSaveLinkTappedAction { 262 TAP_BACK_BUTTON, 263 ROTATE_THEN_TAP_BACK_BUTTON, 264 FINISH_ACTIVITY, 265 LAUNCH_NEW_ACTIVITY, 266 LAUNCH_PREVIOUS_ACTIVITY, 267 TOUCH_OUTSIDE, 268 TAP_NO_ON_SAVE_UI, 269 TAP_YES_ON_SAVE_UI 270 } 271 startActivityOnNewTask(Class<?> clazz)272 protected final void startActivityOnNewTask(Class<?> clazz) { 273 final Intent intent = new Intent(mContext, clazz); 274 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 275 mContext.startActivity(intent); 276 } 277 newTemplate()278 protected RemoteViews newTemplate() { 279 final RemoteViews presentation = new RemoteViews(mPackageName, 280 R.layout.custom_description_with_link); 281 return presentation; 282 } 283 newCustomDescriptionBuilder( Class<? extends Activity> activityClass)284 protected final CustomDescription.Builder newCustomDescriptionBuilder( 285 Class<? extends Activity> activityClass) { 286 final Intent intent = new Intent(mContext, activityClass); 287 intent.setFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_DOCUMENT); 288 return newCustomDescriptionBuilder(intent); 289 } 290 newCustomDescription( Class<? extends Activity> activityClass)291 protected final CustomDescription newCustomDescription( 292 Class<? extends Activity> activityClass) { 293 return newCustomDescriptionBuilder(activityClass).build(); 294 } 295 newCustomDescriptionBuilder(Intent intent)296 protected final CustomDescription.Builder newCustomDescriptionBuilder(Intent intent) { 297 final RemoteViews presentation = newTemplate(); 298 final PendingIntent pendingIntent = 299 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_MUTABLE); 300 presentation.setOnClickPendingIntent(R.id.link, pendingIntent); 301 return new CustomDescription.Builder(presentation); 302 } 303 newCustomDescription(Intent intent)304 protected final CustomDescription newCustomDescription(Intent intent) { 305 return newCustomDescriptionBuilder(intent).build(); 306 } 307 assertSaveUiWithLinkIsShown(int saveType)308 protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType) throws Exception { 309 return assertSaveUiWithLinkIsShown(saveType, "DON'T TAP ME!"); 310 } 311 assertSaveUiWithLinkIsShown(int saveType, String expectedText)312 protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType, String expectedText) 313 throws Exception { 314 // First make sure the UI is shown... 315 final UiObject2 saveUi = mUiBot.assertSaveShowing(saveType); 316 // Then make sure it does have the custom view with link on it... 317 final UiObject2 link = getLink(saveUi); 318 assertThat(link.getText()).isEqualTo(expectedText); 319 return saveUi; 320 } 321 getLink(final UiObject2 container)322 protected final UiObject2 getLink(final UiObject2 container) { 323 final UiObject2 link = container.findObject(By.res(mPackageName, ID_LINK)); 324 assertThat(link).isNotNull(); 325 return link; 326 } 327 tapSaveUiLink(UiObject2 saveUi)328 protected final void tapSaveUiLink(UiObject2 saveUi) { 329 getLink(saveUi).click(); 330 } 331 } 332