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