1 /*
2  * Copyright (C) 2020 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 com.example.android.inlinefillservice;
18 
19 import android.app.PendingIntent;
20 import android.app.slice.Slice;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.graphics.drawable.Icon;
24 import android.service.autofill.Dataset;
25 import android.service.autofill.FillRequest;
26 import android.service.autofill.InlinePresentation;
27 import android.text.TextUtils;
28 import android.util.ArrayMap;
29 import android.view.autofill.AutofillId;
30 import android.view.inputmethod.InlineSuggestionsRequest;
31 import android.widget.inline.InlinePresentationSpec;
32 
33 import androidx.autofill.inline.v1.InlineSuggestionUi;
34 import androidx.autofill.inline.v1.InlineSuggestionUi.Content;
35 
36 import java.util.Optional;
37 
38 public class InlineRequestHelper {
getInlineSuggestionsRequest(FillRequest request)39     static Optional<InlineSuggestionsRequest> getInlineSuggestionsRequest(FillRequest request) {
40         final InlineSuggestionsRequest inlineRequest = request.getInlineSuggestionsRequest();
41         if (inlineRequest != null && inlineRequest.getMaxSuggestionCount() > 0
42                 && !inlineRequest.getInlinePresentationSpecs().isEmpty()) {
43             return Optional.of(inlineRequest);
44         }
45         return Optional.empty();
46     }
47 
getMaxSuggestionCount(Optional<InlineSuggestionsRequest> inlineRequest, int max)48     static int getMaxSuggestionCount(Optional<InlineSuggestionsRequest> inlineRequest, int max) {
49         if (inlineRequest.isPresent()) {
50             return Math.min(max, inlineRequest.get().getMaxSuggestionCount());
51         }
52         return max;
53     }
54 
maybeCreateInlineAuthenticationResponse( Context context, Optional<InlineSuggestionsRequest> inlineRequest)55     static InlinePresentation maybeCreateInlineAuthenticationResponse(
56             Context context, Optional<InlineSuggestionsRequest> inlineRequest) {
57         if (!inlineRequest.isPresent()) {
58             return null;
59         }
60         final PendingIntent attribution = createAttribution(context,
61                 "Please tap on the chip to authenticate the Autofill response.");
62         final Slice slice = createSlice("Tap to auth response", null, null, null, attribution);
63         final InlinePresentationSpec spec = inlineRequest.get().getInlinePresentationSpecs().get(0);
64         return new InlinePresentation(slice, spec, false);
65     }
66 
createInlineDataset(Context context, InlineSuggestionsRequest inlineRequest, String value, int index)67     static InlinePresentation createInlineDataset(Context context,
68             InlineSuggestionsRequest inlineRequest, String value, int index) {
69         final PendingIntent attribution = createAttribution(context,
70                 "Please tap on the chip to autofill the value:" + value);
71         final Slice slice = createSlice(value, null, null, null, attribution);
72         index = Math.min(inlineRequest.getInlinePresentationSpecs().size() - 1, index);
73         final InlinePresentationSpec spec = inlineRequest.getInlinePresentationSpecs().get(index);
74         return new InlinePresentation(slice, spec, false);
75     }
76 
createInlineActionDataset(Context context, ArrayMap<String, AutofillId> fields, InlineSuggestionsRequest inlineRequest, int drawable)77     static Dataset createInlineActionDataset(Context context,
78             ArrayMap<String, AutofillId> fields,
79             InlineSuggestionsRequest inlineRequest, int drawable) {
80         PendingIntent pendingIntent =
81                 PendingIntent.getActivity(context, 0, new Intent(context, SettingsActivity.class),
82                         PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
83 
84         Dataset.Builder builder =
85                 new Dataset.Builder()
86                         .setInlinePresentation(createInlineAction(context, inlineRequest, drawable))
87                         .setAuthentication(pendingIntent.getIntentSender());
88         for (AutofillId fieldId : fields.values()) {
89             builder.setValue(fieldId, null);
90         }
91         return builder.build();
92     }
93 
createInlineAction(Context context, InlineSuggestionsRequest inlineRequest, int drawable)94     private static InlinePresentation createInlineAction(Context context,
95             InlineSuggestionsRequest inlineRequest, int drawable) {
96         final PendingIntent attribution = createAttribution(context,
97                 "Please tap on the chip to launch the action.");
98         final Icon icon = Icon.createWithResource(context, drawable);
99         final Slice slice = createSlice(null, null, icon, null, attribution);
100         // Reuse the first spec's height for the inline action size, as there isn't dedicated
101         // value from the request for this.
102         final InlinePresentationSpec spec = inlineRequest.getInlinePresentationSpecs().get(0);
103         return new InlinePresentation(slice, spec, true);
104     }
105 
createSlice( String title, String subtitle, Icon startIcon, Icon endIcon, PendingIntent attribution)106     private static Slice createSlice(
107             String title, String subtitle, Icon startIcon, Icon endIcon,
108             PendingIntent attribution) {
109         Content.Builder builder = InlineSuggestionUi.newContentBuilder(attribution);
110         if (!TextUtils.isEmpty(title)) {
111             builder.setTitle(title);
112         }
113         if (!TextUtils.isEmpty(subtitle)) {
114             builder.setSubtitle(subtitle);
115         }
116         if (startIcon != null) {
117             builder.setStartIcon(startIcon);
118         }
119         if (endIcon != null) {
120             builder.setEndIcon(endIcon);
121         }
122         return builder.build().getSlice();
123     }
124 
createAttribution(Context context, String msg)125     private static PendingIntent createAttribution(Context context, String msg) {
126         Intent intent = new Intent(context, AttributionDialogActivity.class);
127         intent.putExtra(AttributionDialogActivity.KEY_MSG, msg);
128         // Should use different request code to avoid the new intent overriding the old one.
129         PendingIntent pendingIntent = PendingIntent.getActivity(context, msg.hashCode(), intent,
130                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
131         return pendingIntent;
132     }
133 
134 }
135