• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 foo.bar.fill;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.PendingIntent;
22 import android.app.assist.AssistStructure;
23 import android.app.assist.AssistStructure.WindowNode;
24 import android.app.assist.AssistStructure.ViewNode;
25 import android.content.Intent;
26 import android.content.IntentSender;
27 import android.os.CancellationSignal;
28 import android.service.autofill.AutofillService;
29 import android.service.autofill.Dataset;
30 import android.service.autofill.FillCallback;
31 import android.service.autofill.FillRequest;
32 import android.service.autofill.FillResponse;
33 import android.service.autofill.SaveCallback;
34 import android.service.autofill.SaveInfo;
35 import android.service.autofill.SaveRequest;
36 import android.view.View;
37 import android.view.autofill.AutofillId;
38 import android.view.autofill.AutofillValue;
39 import android.widget.RemoteViews;
40 
41 import java.util.function.Predicate;
42 
43 import foo.bar.fill.R;
44 
45 public class FillService extends AutofillService {
46     static final boolean TEST_RESPONSE_AUTH = false;
47 
48     public static final String RESPONSE_ID = "RESPONSE_ID";
49 
50     static final String DATASET1_NAME = "Foo";
51     static final String DATASET1_USERNAME = "Foo";
52     static final String DATASET1_PASSWORD = "1";
53 
54     static final String DATASET2_NAME = "Bar";
55     static final String DATASET2_USERNAME = "Bar";
56     static final String DATASET2_PASSWORD = "12";
57 
58     static final String DATASET3_NAME = "Baz";
59     static final String DATASET3_USERNAME = "Baz";
60     static final String DATASET3_PASSWORD = "123";
61 
62     static final String DATASET4_NAME = "Bam";
63     static final String DATASET4_USERNAME = "Bam";
64     static final String DATASET4_PASSWORD = "1234";
65 
66     static final String DATASET5_NAME = "Bak";
67     static final String DATASET5_USERNAME = "Bak";
68     static final String DATASET5_PASSWORD = "12345";
69 
70     static final String EXTRA_RESPONSE_ID = "foo.bar.fill.extra.RESPONSE_ID";
71     static final String EXTRA_DATASET_ID = "foo.bar.fill.extra.DATASET_ID";
72 
73     @Override
onFillRequest(@onNull FillRequest request, @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback)74     public void onFillRequest(@NonNull FillRequest request,
75             @NonNull CancellationSignal cancellationSignal,
76             @NonNull FillCallback callback) {
77         AssistStructure structure = request.getFillContexts().get(0).getStructure();
78 
79         ViewNode username = findUsername(structure);
80         ViewNode password = findPassword(structure);
81 
82         if (username != null && password != null) {
83             final FillResponse response;
84 
85             if (TEST_RESPONSE_AUTH) {
86                 Intent intent = new Intent(this, AuthActivity.class);
87                 intent.putExtra(EXTRA_RESPONSE_ID, RESPONSE_ID);
88                 IntentSender sender = PendingIntent.getActivity(this, 0, intent,
89                         PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT)
90                         .getIntentSender();
91 
92                 RemoteViews presentation = new RemoteViews(getPackageName(), R.layout.pathology);
93 
94 //                presentation.setTextViewText(R.id.text1, "First");
95 //                Intent firstIntent = new Intent(this, FirstActivity.class);
96 //                presentation.setOnClickPendingIntent(R.id.text1, PendingIntent.getActivity(
97 //                        this, 0, firstIntent, PendingIntent.FLAG_CANCEL_CURRENT));
98 
99 //                presentation.setTextViewText(R.id.text2, "Second");
100 //                Intent secondIntent = new Intent(this, SecondActivity.class);
101 //                presentation.setOnClickPendingIntent(R.id.text2, PendingIntent.getActivity(
102 //                        this, 0, secondIntent, PendingIntent.FLAG_CANCEL_CURRENT));
103 
104                 response = new FillResponse.Builder()
105                         .setAuthentication(new AutofillId[]{username.getAutofillId(),
106                                 password.getAutofillId()}, sender, presentation)
107                         .build();
108             } else {
109                 Intent intent = new Intent(this, AuthActivity.class);
110                 intent.putExtra(EXTRA_DATASET_ID, DATASET1_NAME);
111                 IntentSender sender = PendingIntent.getActivity(this, 0, intent,
112                         PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT)
113                         .getIntentSender();
114 
115                 RemoteViews presentation1 = new RemoteViews(getPackageName(), R.layout.list_item);
116                 presentation1.setTextViewText(R.id.text1, DATASET1_NAME);
117 
118                 RemoteViews presentation2 = new RemoteViews(getPackageName(), R.layout.list_item);
119                 presentation2.setTextViewText(R.id.text1, DATASET2_NAME);
120 
121                 RemoteViews presentation3 = new RemoteViews(getPackageName(), R.layout.list_item);
122                 presentation3.setTextViewText(R.id.text1, DATASET3_NAME);
123 
124                 RemoteViews presentation4 = new RemoteViews(getPackageName(), R.layout.list_item);
125                 presentation4.setTextViewText(R.id.text1, DATASET4_NAME);
126 
127                 RemoteViews presentation5 = new RemoteViews(getPackageName(), R.layout.list_item);
128                 presentation5.setTextViewText(R.id.text1, /*DATASET5_NAME*/ "Auth needed");
129 
130                 response = new FillResponse.Builder()
131                         .addDataset(new Dataset.Builder(presentation1)
132                                 .setValue(username.getAutofillId(),
133                                         AutofillValue.forText(DATASET1_USERNAME))
134                                 .setValue(password.getAutofillId(),
135                                         AutofillValue.forText(DATASET1_PASSWORD))
136                                 .build())
137                         .addDataset(new Dataset.Builder(presentation2)
138                                 .setValue(username.getAutofillId(),
139                                         AutofillValue.forText(DATASET2_USERNAME))
140                                 .setValue(password.getAutofillId(),
141                                         AutofillValue.forText(DATASET2_PASSWORD))
142 //                                .setAuthentication(sender)
143                                 .build())
144                         .addDataset(new Dataset.Builder(presentation3)
145                                 .setValue(username.getAutofillId(),
146                                         AutofillValue.forText(DATASET3_USERNAME))
147                                 .setValue(password.getAutofillId(),
148                                         AutofillValue.forText(DATASET3_PASSWORD))
149 //                                .setAuthentication(sender)
150                                 .build())
151                         .addDataset(new Dataset.Builder(presentation4)
152                                 .setValue(username.getAutofillId(),
153                                         AutofillValue.forText(DATASET4_USERNAME))
154                                 .setValue(password.getAutofillId(),
155                                         AutofillValue.forText(DATASET4_PASSWORD))
156 //                                .setAuthentication(sender)
157                                 .build())
158                         .addDataset(new Dataset.Builder(presentation5)
159                                 .setValue(username.getAutofillId(),
160                                         AutofillValue.forText(DATASET5_USERNAME))
161                                 .setValue(password.getAutofillId(),
162                                         AutofillValue.forText(DATASET5_PASSWORD))
163                                 .setAuthentication(sender)
164                                 .build())
165                         .setSaveInfo(new SaveInfo.Builder(
166                                 SaveInfo.SAVE_DATA_TYPE_PASSWORD
167                                         | SaveInfo.SAVE_DATA_TYPE_USERNAME,
168                                 new AutofillId[] {username.getAutofillId(),
169                                         password.getAutofillId()})
170                                 .build())
171                         .build();
172             }
173 
174             callback.onSuccess(response);
175         } else {
176             callback.onFailure("Whoops");
177         }
178     }
179 
180     @Override
onSaveRequest(@onNull SaveRequest request, @NonNull SaveCallback callback)181     public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) {
182         AssistStructure structure = request.getFillContexts().get(0).getStructure();
183         ViewNode username = findUsername(structure);
184         ViewNode password = findPassword(structure);
185     }
186 
findUsername(AssistStructure structure)187     static ViewNode findUsername(AssistStructure structure) {
188         return findByPredicate(structure, (node) ->
189             node.getAutofillType() == View.AUTOFILL_TYPE_TEXT
190                     && "username".equals(node.getIdEntry())
191         );
192     }
193 
findPassword(AssistStructure structure)194     static ViewNode findPassword(AssistStructure structure) {
195         return findByPredicate(structure, (node) ->
196                 node.getAutofillType() == View.AUTOFILL_TYPE_TEXT
197                         && "password".equals(node.getIdEntry())
198         );
199     }
200 
findByPredicate(AssistStructure structure, Predicate<ViewNode> predicate)201     private static ViewNode findByPredicate(AssistStructure structure,
202             Predicate<ViewNode> predicate) {
203         final int windowCount = structure.getWindowNodeCount();
204         for (int i = 0; i < windowCount; i++) {
205             WindowNode window = structure.getWindowNodeAt(i);
206             ViewNode root = window.getRootViewNode();
207             if (root == null) {
208                 return null;
209             }
210             ViewNode node = findByPredicate(root, predicate);
211             if (node != null) {
212                 return node;
213             }
214         }
215         return null;
216     }
217 
findByPredicate(ViewNode root, Predicate<ViewNode> predicate)218     private static ViewNode findByPredicate(ViewNode root, Predicate<ViewNode> predicate) {
219         if (root == null) {
220             return null;
221         }
222         if (predicate.test(root)) {
223             return root;
224         }
225         final int childCount = root.getChildCount();
226         for (int i = 0; i < childCount; i++) {
227             ViewNode child = root.getChildAt(i);
228             ViewNode node = findByPredicate(child, predicate);
229             if (node != null) {
230                 return node;
231             }
232         }
233         return null;
234     }
235 }
236