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