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.support.test.uiautomator.By;
31 import android.support.test.uiautomator.UiObject2;
32 import android.widget.RemoteViews;
33 
34 import androidx.annotation.NonNull;
35 import androidx.test.filters.FlakyTest;
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 
97         mUiBot.assumeMinimumResolution(500);
98         mUiBot.setScreenOrientation(UiBot.PORTRAIT);
99         try {
100             saveUiRestoredAfterTappingLinkTest(
101                     PostSaveLinkTappedAction.ROTATE_THEN_TAP_BACK_BUTTON);
102         } finally {
103             try {
104                 mUiBot.setScreenOrientation(UiBot.PORTRAIT);
105                 cleanUpAfterScreenOrientationIsBackToPortrait();
106             } catch (Exception e) {
107                 mSafeCleanerRule.add(e);
108             } finally {
109                 mUiBot.resetScreenResolution();
110             }
111         }
112     }
113 
114     /**
115      * Tests scenarios when user taps a link in the custom description, then the new activity
116      * finishes:
117      * the Save UI should have been restored.
118      */
119     @Test
testTapLink_finishActivity()120     public final void testTapLink_finishActivity() throws Exception {
121         saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction.FINISH_ACTIVITY);
122     }
123 
saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction type)124     protected abstract void saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction type)
125             throws Exception;
126 
cleanUpAfterScreenOrientationIsBackToPortrait()127     protected void cleanUpAfterScreenOrientationIsBackToPortrait() throws Exception {
128     }
129 
130     /**
131      * Tests scenarios when user taps a link in the custom description, taps back to return to the
132      * activity with the Save UI, and touch outside the Save UI to dismiss it.
133      *
134      * <p>Then user starts a new session by focusing in a field.
135      */
136     @Test
testTapLink_tapBack_thenStartOverByTouchOutsideAndFocus()137     public final void testTapLink_tapBack_thenStartOverByTouchOutsideAndFocus()
138             throws Exception {
139         tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TOUCH_OUTSIDE, false);
140     }
141 
142     /**
143      * Tests scenarios when user taps a link in the custom description, taps back to return to the
144      * activity with the Save UI, and touch outside the Save UI to dismiss it.
145      *
146      * <p>Then user starts a new session by forcing autofill.
147      */
148     @Test
testTapLink_tapBack_thenStartOverByTouchOutsideAndManualRequest()149     public void testTapLink_tapBack_thenStartOverByTouchOutsideAndManualRequest()
150             throws Exception {
151         tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TOUCH_OUTSIDE, true);
152     }
153 
154     /**
155      * Tests scenarios when user taps a link in the custom description, taps back to return to the
156      * activity with the Save UI, and tap the "No" button to dismiss it.
157      *
158      * <p>Then user starts a new session by focusing in a field.
159      */
160     @Test
testTapLink_tapBack_thenStartOverBySayingNoAndFocus()161     public final void testTapLink_tapBack_thenStartOverBySayingNoAndFocus()
162             throws Exception {
163         tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_NO_ON_SAVE_UI,
164                 false);
165     }
166 
167     /**
168      * Tests scenarios when user taps a link in the custom description, taps back to return to the
169      * activity with the Save UI, and tap the "No" button to dismiss it.
170      *
171      * <p>Then user starts a new session by forcing autofill.
172      */
173     @Test
testTapLink_tapBack_thenStartOverBySayingNoAndManualRequest()174     public final void testTapLink_tapBack_thenStartOverBySayingNoAndManualRequest()
175             throws Exception {
176         tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_NO_ON_SAVE_UI, true);
177     }
178 
179     /**
180      * Tests scenarios when user taps a link in the custom description, taps back to return to the
181      * activity with the Save UI, and the "Yes" button to save it.
182      *
183      * <p>Then user starts a new session by focusing in a field.
184      */
185     @Test
testTapLink_tapBack_thenStartOverBySayingYesAndFocus()186     public final void testTapLink_tapBack_thenStartOverBySayingYesAndFocus()
187             throws Exception {
188         tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_YES_ON_SAVE_UI,
189                 false);
190     }
191 
192     /**
193      * Tests scenarios when user taps a link in the custom description, taps back to return to the
194      * activity with the Save UI, and the "Yes" button to save it.
195      *
196      * <p>Then user starts a new session by forcing autofill.
197      */
198     @Test
testTapLink_tapBack_thenStartOverBySayingYesAndManualRequest()199     public final void testTapLink_tapBack_thenStartOverBySayingYesAndManualRequest()
200             throws Exception {
201         tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_YES_ON_SAVE_UI, true);
202     }
203 
tapLinkThenTapBackThenStartOverTest( PostSaveLinkTappedAction action, boolean manualRequest)204     protected abstract void tapLinkThenTapBackThenStartOverTest(
205             PostSaveLinkTappedAction action, boolean manualRequest) throws Exception;
206 
207     /**
208      * Tests scenarios when user taps a link in the custom description, then re-launches the
209      * original activity:
210      * the Save UI should have been canceled.
211      */
212     @Test
testTapLink_backToPreviousActivityByLaunchingIt()213     public final void testTapLink_backToPreviousActivityByLaunchingIt()
214             throws Exception {
215         saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction.LAUNCH_PREVIOUS_ACTIVITY);
216     }
217 
218     /**
219      * Tests scenarios when user taps a link in the custom description, then launches a 3rd
220      * activity:
221      * the Save UI should have been canceled.
222      */
223     @Test
testTapLink_launchNewActivityThenTapBack()224     public final void testTapLink_launchNewActivityThenTapBack() throws Exception {
225         saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction.LAUNCH_NEW_ACTIVITY);
226     }
227 
saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction type)228     protected abstract void saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction type)
229             throws Exception;
230 
231     @Test
232     @FlakyTest(bugId = 177259617)
testTapLink_launchTrampolineActivityThenTapBackAndStartNewSession()233     public final void testTapLink_launchTrampolineActivityThenTapBackAndStartNewSession()
234             throws Exception {
235         // Reset AutofillOptions to avoid cts package was added to augmented autofill allowlist.
236         Helper.resetApplicationAutofillOptions(sContext);
237 
238         tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest();
239 
240         // Clear AutofillOptions.
241         Helper.clearApplicationAutofillOptions(sContext);
242     }
243 
tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest()244     protected abstract void tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest()
245             throws Exception;
246 
247     @Test
testTapLinkAfterUpdateAppliedToLinkView()248     public final void testTapLinkAfterUpdateAppliedToLinkView() throws Exception {
249         tapLinkAfterUpdateAppliedTest(true);
250     }
251 
252     @Test
testTapLinkAfterUpdateAppliedToAnotherView()253     public final void testTapLinkAfterUpdateAppliedToAnotherView() throws Exception {
254         tapLinkAfterUpdateAppliedTest(false);
255     }
256 
tapLinkAfterUpdateAppliedTest(boolean updateLinkView)257     protected abstract void tapLinkAfterUpdateAppliedTest(boolean updateLinkView) throws Exception;
258 
259     public enum PostSaveLinkTappedAction {
260         TAP_BACK_BUTTON,
261         ROTATE_THEN_TAP_BACK_BUTTON,
262         FINISH_ACTIVITY,
263         LAUNCH_NEW_ACTIVITY,
264         LAUNCH_PREVIOUS_ACTIVITY,
265         TOUCH_OUTSIDE,
266         TAP_NO_ON_SAVE_UI,
267         TAP_YES_ON_SAVE_UI
268     }
269 
startActivityOnNewTask(Class<?> clazz)270     protected final void startActivityOnNewTask(Class<?> clazz) {
271         final Intent intent = new Intent(mContext, clazz);
272         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
273         mContext.startActivity(intent);
274     }
275 
newTemplate()276     protected RemoteViews newTemplate() {
277         final RemoteViews presentation = new RemoteViews(mPackageName,
278                 R.layout.custom_description_with_link);
279         return presentation;
280     }
281 
newCustomDescriptionBuilder( Class<? extends Activity> activityClass)282     protected final CustomDescription.Builder newCustomDescriptionBuilder(
283             Class<? extends Activity> activityClass) {
284         final Intent intent = new Intent(mContext, activityClass);
285         intent.setFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
286         return newCustomDescriptionBuilder(intent);
287     }
288 
newCustomDescription( Class<? extends Activity> activityClass)289     protected final CustomDescription newCustomDescription(
290             Class<? extends Activity> activityClass) {
291         return newCustomDescriptionBuilder(activityClass).build();
292     }
293 
newCustomDescriptionBuilder(Intent intent)294     protected final CustomDescription.Builder newCustomDescriptionBuilder(Intent intent) {
295         final RemoteViews presentation = newTemplate();
296         final PendingIntent pendingIntent =
297                 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_MUTABLE);
298         presentation.setOnClickPendingIntent(R.id.link, pendingIntent);
299         return new CustomDescription.Builder(presentation);
300     }
301 
newCustomDescription(Intent intent)302     protected final CustomDescription newCustomDescription(Intent intent) {
303         return newCustomDescriptionBuilder(intent).build();
304     }
305 
assertSaveUiWithLinkIsShown(int saveType)306     protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType) throws Exception {
307         return assertSaveUiWithLinkIsShown(saveType, "DON'T TAP ME!");
308     }
309 
assertSaveUiWithLinkIsShown(int saveType, String expectedText)310     protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType, String expectedText)
311             throws Exception {
312         // First make sure the UI is shown...
313         final UiObject2 saveUi = mUiBot.assertSaveShowing(saveType);
314         // Then make sure it does have the custom view with link on it...
315         final UiObject2 link = getLink(saveUi);
316         assertThat(link.getText()).isEqualTo(expectedText);
317         return saveUi;
318     }
319 
getLink(final UiObject2 container)320     protected final UiObject2 getLink(final UiObject2 container) {
321         final UiObject2 link = container.findObject(By.res(mPackageName, ID_LINK));
322         assertThat(link).isNotNull();
323         return link;
324     }
325 
tapSaveUiLink(UiObject2 saveUi)326     protected final void tapSaveUiLink(UiObject2 saveUi) {
327         getLink(saveUi).click();
328     }
329 }
330