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 17 package android.autofillservice.cts; 18 19 import static android.autofillservice.cts.Helper.findNodeByResourceId; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import android.app.assist.AssistStructure; 24 import android.platform.test.annotations.AppModeFull; 25 import android.support.test.runner.AndroidJUnit4; 26 import android.view.View; 27 import android.view.autofill.AutofillValue; 28 import android.widget.EditText; 29 30 import androidx.annotation.IdRes; 31 import androidx.annotation.NonNull; 32 import androidx.annotation.Nullable; 33 34 import org.junit.Before; 35 import org.junit.Rule; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 39 import java.util.function.Consumer; 40 41 @RunWith(AndroidJUnit4.class) 42 @AppModeFull // Unit test 43 public class ViewAttributesTest extends AutoFillServiceTestCase { 44 @Rule 45 public final AutofillActivityTestRule<ViewAttributesTestActivity> mActivityRule = 46 new AutofillActivityTestRule<>(ViewAttributesTestActivity.class); 47 48 private ViewAttributesTestActivity mActivity; 49 50 @Before 51 public void setActivity() { 52 mActivity = mActivityRule.getActivity(); 53 } 54 55 @Nullable private String[] getHintsFromView(@IdRes int resId) { 56 return mActivity.findViewById(resId).getAutofillHints(); 57 } 58 59 private void checkEditTextNoHint(@Nullable String[] hints) { 60 assertThat(hints).isNull(); 61 } 62 63 private void checkEditTextHintCustom(@Nullable String[] hints) { 64 assertThat(hints).isEqualTo( 65 new String[] {mActivity.getString(R.string.new_password_label)}); 66 } 67 68 private void checkEditTextPassword(@Nullable String[] hints) { 69 assertThat(hints).isEqualTo(new String[] {View.AUTOFILL_HINT_PASSWORD}); 70 } 71 72 private void checkEditTextPhoneName(@Nullable String[] hints) { 73 assertThat(hints).isEqualTo( 74 new String[] {View.AUTOFILL_HINT_PHONE, View.AUTOFILL_HINT_USERNAME}); 75 } 76 77 private void checkEditTextHintsFromArray(@Nullable String[] hints) { 78 assertThat(hints).isEqualTo(new String[] {"yesterday", "today", "tomorrow", "never"}); 79 } 80 81 @Test 82 public void checkViewHints() { 83 checkEditTextNoHint(getHintsFromView(R.id.editTextNoHint)); 84 checkEditTextHintCustom(getHintsFromView(R.id.editTextHintCustom)); 85 checkEditTextPassword(getHintsFromView(R.id.editTextPassword)); 86 checkEditTextPhoneName(getHintsFromView(R.id.editTextPhoneName)); 87 checkEditTextHintsFromArray(getHintsFromView(R.id.editTextHintsFromArray)); 88 } 89 90 @Test 91 public void checkSetAutoFill() { 92 View v = mActivity.findViewById(R.id.editTextNoHint); 93 94 v.setAutofillHints((String[]) null); 95 assertThat(v.getAutofillHints()).isNull(); 96 97 v.setAutofillHints(new String[0]); 98 assertThat(v.getAutofillHints()).isNull(); 99 100 v.setAutofillHints(new String[]{View.AUTOFILL_HINT_PASSWORD}); 101 assertThat(v.getAutofillHints()).isEqualTo(new String[]{View.AUTOFILL_HINT_PASSWORD}); 102 103 v.setAutofillHints(new String[]{"custom", "value"}); 104 assertThat(v.getAutofillHints()).isEqualTo(new String[]{"custom", "value"}); 105 106 v.setAutofillHints("more", "values"); 107 assertThat(v.getAutofillHints()).isEqualTo(new String[]{"more", "values"}); 108 109 v.setAutofillHints( 110 new String[]{View.AUTOFILL_HINT_PASSWORD, View.AUTOFILL_HINT_EMAIL_ADDRESS}); 111 assertThat(v.getAutofillHints()).isEqualTo(new String[]{View.AUTOFILL_HINT_PASSWORD, 112 View.AUTOFILL_HINT_EMAIL_ADDRESS}); 113 } 114 115 /** 116 * Wait for autofill to be initialized and trigger autofill on a view. 117 * 118 * @param view The view to trigger the autofill on 119 * 120 * @return The {@link InstrumentedAutoFillService.FillRequest triggered} 121 */ 122 private InstrumentedAutoFillService.FillRequest startAutoFill(boolean forceAutofill, 123 @NonNull View view) throws Exception { 124 if (forceAutofill) { 125 mActivity.getAutofillManager().requestAutofill(view); 126 } else { 127 mActivity.syncRunOnUiThread(() -> { 128 view.clearFocus(); 129 view.requestFocus(); 130 }); 131 } 132 133 InstrumentedAutoFillService.waitUntilConnected(); 134 135 return sReplier.getNextFillRequest(); 136 } 137 138 @Nullable private String[] getHintsFromStructure(@NonNull AssistStructure structure, 139 @NonNull String resName) { 140 return findNodeByResourceId(structure, resName).getAutofillHints(); 141 } 142 143 private void onAssistStructure(boolean forceAutofill, @NonNull Consumer<AssistStructure> test) 144 throws Exception { 145 EditText editTextNoHint = mActivity.findViewById(R.id.editTextNoHint); 146 mActivity.syncRunOnUiThread(() -> editTextNoHint.setVisibility(View.VISIBLE)); 147 148 // Set service. 149 enableService(); 150 151 // Set expectations. 152 sReplier.addResponse(new CannedFillResponse.CannedDataset.Builder() 153 .setField("editTextNoHint", AutofillValue.forText("filled")) 154 .setPresentation(createPresentation("dataset")) 155 .build()); 156 157 // Trigger autofill. 158 InstrumentedAutoFillService.FillRequest request = startAutoFill(forceAutofill, 159 editTextNoHint); 160 161 assertThat(request.contexts.size()).isEqualTo(1); 162 test.accept(request.contexts.get(0).getStructure()); 163 } 164 165 @Test 166 public void checkAssistStructureHints() throws Exception { 167 onAssistStructure(false, (structure) -> { 168 // Check autofill hints analogue to #checkViewHints 169 checkEditTextNoHint(getHintsFromStructure(structure, "editTextNoHint")); 170 checkEditTextHintCustom(getHintsFromStructure(structure, "editTextHintCustom")); 171 checkEditTextPassword(getHintsFromStructure(structure, "editTextPassword")); 172 checkEditTextPhoneName(getHintsFromStructure(structure, "editTextPhoneName")); 173 checkEditTextHintsFromArray(getHintsFromStructure(structure, 174 "editTextHintsFromArray")); 175 } 176 ); 177 } 178 179 @Test 180 public void checkViewLocationInAssistStructure() throws Exception { 181 onAssistStructure(false, (structure) -> { 182 // check size of outerView 183 AssistStructure.ViewNode outerView = findNodeByResourceId(structure, 184 "outerView"); 185 186 // The size of the view should include all paddings and size of all children 187 assertThat(outerView.getHeight()).isEqualTo( 188 2 // outerView.top 189 + 11 // nestedView.top 190 + 23 // doubleNestedView.top 191 + 41 // tripleNestedView.height 192 + 47 // secondDoubleNestedView.height 193 + 31 // doubleNestedView.bottom 194 + 17 // nestedView.bottom 195 + 5); // outerView.bottom 196 assertThat(outerView.getWidth()).isEqualTo( 197 7 // outerView.left 198 + 19 // nestedView.left 199 + Math.max(37 // doubleNestedView.left 200 + 43 // tripleNestedView.width 201 + 29, // doubleNestedView.right 202 53) // secondDoubleNestedView.width 203 + 13 // nestedView.right 204 + 3); // outerView.right 205 206 207 // The nestedView is suppressed, hence the structure should be 208 // 209 // outerView 210 // doubleNestedView left=26 top=13 211 // tripleNestedView left=37 top=23 212 // secondDoubleNestedView left=26 top=108 213 214 assertThat(outerView.getChildCount()).isEqualTo(2); 215 AssistStructure.ViewNode doubleNestedView; 216 AssistStructure.ViewNode secondDoubleNestedView; 217 if (outerView.getChildAt(0).getIdEntry().equals("doubleNestedView")) { 218 doubleNestedView = outerView.getChildAt(0); 219 secondDoubleNestedView = outerView.getChildAt(1); 220 } else { 221 secondDoubleNestedView = outerView.getChildAt(0); 222 doubleNestedView = outerView.getChildAt(1); 223 } 224 assertThat(doubleNestedView.getIdEntry()).isEqualTo("doubleNestedView"); 225 assertThat(secondDoubleNestedView.getIdEntry()).isEqualTo 226 ("secondDoubleNestedView"); 227 228 // The location of the doubleNestedView should include all suppressed parent's 229 // offset 230 assertThat(doubleNestedView.getLeft()).isEqualTo( 231 7 // outerView.left 232 + 19); // nestedView.left 233 assertThat(doubleNestedView.getTop()).isEqualTo( 234 2 // outerView.top 235 + 11); // nestedView.top 236 237 // The location of the tripleNestedView should be relative to it's parent 238 assertThat(doubleNestedView.getChildCount()).isEqualTo(1); 239 AssistStructure.ViewNode tripleNestedView = doubleNestedView.getChildAt(0); 240 assertThat(doubleNestedView.getIdEntry()).isEqualTo("doubleNestedView"); 241 242 assertThat(tripleNestedView.getLeft()).isEqualTo(37); // doubleNestedView.left 243 assertThat(tripleNestedView.getTop()).isEqualTo(23); // doubleNestedView.top 244 } 245 ); 246 } 247 248 @Test 249 public void checkViewLocationInAssistStructureAfterForceAutofill() throws Exception { 250 onAssistStructure(true, (structure) -> { 251 AssistStructure.ViewNode outerView = findNodeByResourceId(structure, 252 "outerView"); 253 254 // The structure should be 255 // 256 // outerView 257 // nestedView left=7 top=2 258 // doubleNestedView left=19 top=11 259 // tripleNestedView left=37 top=23 260 // secondDoubleNestedView left=19 top=106 261 262 // Test only what is different from #checkViewLocationInAssistStructure 263 assertThat(outerView.getChildCount()).isEqualTo(1); 264 265 AssistStructure.ViewNode nestedView = outerView.getChildAt(0); 266 assertThat(nestedView.getIdEntry()).isEqualTo("nestedView"); 267 assertThat(nestedView.getLeft()).isEqualTo(7); // outerView.left 268 assertThat(nestedView.getTop()).isEqualTo(2); // outerView.top 269 270 assertThat(nestedView.getChildCount()).isEqualTo(2); 271 AssistStructure.ViewNode doubleNestedView; 272 if (nestedView.getChildAt(0).getIdEntry().equals("doubleNestedView")) { 273 doubleNestedView = nestedView.getChildAt(0); 274 } else { 275 doubleNestedView = nestedView.getChildAt(1); 276 } 277 assertThat(doubleNestedView.getIdEntry()).isEqualTo("doubleNestedView"); 278 279 assertThat(doubleNestedView.getLeft()).isEqualTo(19); // nestedView.left 280 assertThat(doubleNestedView.getTop()).isEqualTo(11); // nestedView.top 281 } 282 ); 283 } 284 } 285