1 /* 2 * Copyright (C) 2023 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 20 import android.app.assist.AssistStructure; 21 import android.service.assist.classification.FieldClassification; 22 import android.service.assist.classification.FieldClassificationResponse; 23 import android.util.Log; 24 import android.view.autofill.AutofillId; 25 26 import androidx.annotation.NonNull; 27 28 import java.util.HashSet; 29 import java.util.Set; 30 import java.util.function.Function; 31 32 /** 33 * Helper class used to produce a 34 * {@link android.service.assist.classification.FieldClassificationResponse} based on expected 35 * fields that should be present in the {@link AssistStructure}. 36 * 37 * <p>Typical usage: 38 * 39 * <pre class="prettyprint"> 40 * InstrumentedFieldClassificationService.getReplier().addResponse( 41 * new CannedFieldClassificationResponse.Builder() 42 * .addFieldClassification( 43 * new CannedFieldClassificationResponse.CannedFieldClassification( 44 * "resource_id1", Set.of("autofill_hint_1"))) 45 * .build()); 46 * </pre class="prettyprint"> 47 */ 48 public final class CannedFieldClassificationResponse { 49 private static final String TAG = CannedFieldClassificationResponse.class.getSimpleName(); 50 private Set<CannedFieldClassification> mCannedFieldClassifications = new HashSet<>(); 51 CannedFieldClassificationResponse(Builder builder)52 private CannedFieldClassificationResponse(Builder builder) { 53 mCannedFieldClassifications = builder.mClassifications; 54 } 55 56 public static final class Builder { 57 private Set<CannedFieldClassification> mClassifications = new HashSet<>(); 58 addFieldClassification(CannedFieldClassification classification)59 public Builder addFieldClassification(CannedFieldClassification classification) { 60 mClassifications.add(classification); 61 return this; 62 } 63 build()64 public CannedFieldClassificationResponse build() { 65 return new CannedFieldClassificationResponse(this); 66 } 67 } 68 69 /** 70 * Creates a new response, replacing the dataset field ids by the real ids from the assist 71 * structure. 72 */ asResponse( @onNull Function<String, AssistStructure.ViewNode> nodeResolver)73 public FieldClassificationResponse asResponse( 74 @NonNull Function<String, AssistStructure.ViewNode> nodeResolver) { 75 return asResponseWithAutofillId((id)-> { 76 AssistStructure.ViewNode node = nodeResolver.apply(id); 77 if (node == null) { 78 throw new AssertionError("No node with resource id " + id); 79 } 80 return node.getAutofillId(); 81 }); 82 } 83 84 /** 85 * Creates a new response, replacing the dataset field ids by the real ids from the assist 86 * structure. 87 */ asResponseWithAutofillId( @onNull Function<String, AutofillId> autofillIdResolver)88 private FieldClassificationResponse asResponseWithAutofillId( 89 @NonNull Function<String, AutofillId> autofillIdResolver) { 90 Set<FieldClassification> fieldClassifications = new HashSet<>(); 91 for (CannedFieldClassification cannedFieldClassification : mCannedFieldClassifications) { 92 fieldClassifications.add( 93 cannedFieldClassification.asFieldClassification(autofillIdResolver)); 94 } 95 return new FieldClassificationResponse(fieldClassifications); 96 } 97 98 /** 99 * Helper class used to produce a {@link FieldClassification} based on expected fields that 100 * should be present in the {@link AssistStructure}. 101 * 102 * <p>Typical usage: 103 * 104 * <pre class="prettyprint"> 105 * InstrumentedFieldClassificationService.getReplier().addResponse( 106 * new CannedFieldClassificationResponse.Builder() 107 * .addFieldClassification( 108 * new CannedFieldClassificationResponse.CannedFieldClassification( 109 * "resource_id1", Set.of("autofill_hint_1"))) 110 * .build()); 111 * </pre class="prettyprint"> 112 */ 113 public static class CannedFieldClassification { 114 private final Set<String> mHints; 115 private final String mId; 116 CannedFieldClassification(String id, Set<String> hints)117 public CannedFieldClassification(String id, Set<String> hints) { 118 mId = id; 119 mHints = hints; 120 } 121 122 /** 123 * Creates a new dataset, replacing the field ids by the real ids from the assist structure. 124 */ asFieldClassificationtWithNodeResolver( Function<String, AssistStructure.ViewNode> nodeResolver)125 public FieldClassification asFieldClassificationtWithNodeResolver( 126 Function<String, AssistStructure.ViewNode> nodeResolver) { 127 return asFieldClassification((id) -> { 128 AssistStructure.ViewNode node = nodeResolver.apply(id); 129 if (node == null) { 130 throw new AssertionError("No node with resource id " + id); 131 } 132 return node.getAutofillId(); 133 }); 134 } 135 asFieldClassification( Function<String, AutofillId> autofillIdResolver)136 public FieldClassification asFieldClassification( 137 Function<String, AutofillId> autofillIdResolver) { 138 139 final AutofillId autofillId = autofillIdResolver.apply(mId); 140 if (autofillId == null) { 141 throw new AssertionError("No node with resource id " + mId); 142 } 143 FieldClassification response = new FieldClassification(autofillId, mHints); 144 Log.v(TAG, "Response: " + response); 145 return response; 146 } 147 } 148 } 149