blob: 9d4c9ebbf5922e234d85563aeb3874813025b282 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.autofill.ui;
import static android.view.inputmethod.InlineSuggestionInfo.TYPE_SUGGESTION;
import static com.android.server.autofill.Helper.sDebug;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.IntentSender;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.service.autofill.InlinePresentation;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.view.autofill.AutofillManager;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.widget.inline.InlinePresentationSpec;
import com.android.internal.view.inline.IInlineContentProvider;
import java.util.List;
final class InlineSuggestionFactory {
private static final String TAG = "InlineSuggestionFactory";
public static InlineSuggestion createInlineAuthentication(
@NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo, @NonNull FillResponse response,
@NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
InlinePresentation inlineAuthentication = response.getInlinePresentation();
final int requestId = response.getRequestId();
return createInlineSuggestion(inlineFillUiInfo, InlineSuggestionInfo.SOURCE_AUTOFILL,
InlineSuggestionInfo.TYPE_ACTION, () -> uiCallback.authenticate(requestId,
AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
mergedInlinePresentation(inlineFillUiInfo.mInlineRequest, 0, inlineAuthentication),
createInlineSuggestionTooltip(inlineFillUiInfo.mInlineRequest,
inlineFillUiInfo, InlineSuggestionInfo.SOURCE_AUTOFILL,
response.getInlineTooltipPresentation()),
uiCallback);
}
/**
* Creates an array of {@link InlineSuggestion}s with the {@code datasets} provided by either
* regular/augmented autofill services.
*/
@Nullable
public static SparseArray<Pair<Dataset, InlineSuggestion>> createInlineSuggestions(
@NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
@NonNull @InlineSuggestionInfo.Source String suggestionSource,
@NonNull List<Dataset> datasets,
@NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
if (sDebug) Slog.d(TAG, "createInlineSuggestions(source=" + suggestionSource + ") called");
final InlineSuggestionsRequest request = inlineFillUiInfo.mInlineRequest;
SparseArray<Pair<Dataset, InlineSuggestion>> response = new SparseArray<>(datasets.size());
boolean hasTooltip = false;
for (int datasetIndex = 0; datasetIndex < datasets.size(); datasetIndex++) {
final Dataset dataset = datasets.get(datasetIndex);
final int fieldIndex = dataset.getFieldIds().indexOf(inlineFillUiInfo.mFocusedId);
if (fieldIndex < 0) {
Slog.w(TAG, "AutofillId=" + inlineFillUiInfo.mFocusedId + " not found in dataset");
continue;
}
final InlinePresentation inlinePresentation =
dataset.getFieldInlinePresentation(fieldIndex);
if (inlinePresentation == null) {
Slog.w(TAG, "InlinePresentation not found in dataset");
continue;
}
final String suggestionType =
dataset.getAuthentication() == null ? TYPE_SUGGESTION
: InlineSuggestionInfo.TYPE_ACTION;
final int index = datasetIndex;
InlineSuggestion inlineSuggestionTooltip = null;
if (!hasTooltip) {
// Only available for first one inline suggestion tooltip.
inlineSuggestionTooltip = createInlineSuggestionTooltip(request,
inlineFillUiInfo, suggestionSource,
dataset.getFieldInlineTooltipPresentation(fieldIndex));
if (inlineSuggestionTooltip != null) {
hasTooltip = true;
}
}
InlineSuggestion inlineSuggestion = createInlineSuggestion(
inlineFillUiInfo, suggestionSource, suggestionType,
() -> uiCallback.autofill(dataset, index),
mergedInlinePresentation(request, datasetIndex, inlinePresentation),
inlineSuggestionTooltip,
uiCallback);
response.append(datasetIndex, Pair.create(dataset, inlineSuggestion));
}
return response;
}
private static InlineSuggestion createInlineSuggestion(
@NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
@NonNull @InlineSuggestionInfo.Source String suggestionSource,
@NonNull @InlineSuggestionInfo.Type String suggestionType,
@NonNull Runnable onClickAction,
@NonNull InlinePresentation inlinePresentation,
@Nullable InlineSuggestion tooltip,
@NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(), suggestionSource,
inlinePresentation.getAutofillHints(), suggestionType,
inlinePresentation.isPinned(), tooltip);
return new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlineFillUiInfo, inlinePresentation,
onClickAction, uiCallback));
}
/**
* Returns an {@link InlinePresentation} with the style spec from the request/host, and
* everything else from the provided {@code inlinePresentation}.
*/
private static InlinePresentation mergedInlinePresentation(
@NonNull InlineSuggestionsRequest request,
int index, @NonNull InlinePresentation inlinePresentation) {
final List<InlinePresentationSpec> specs = request.getInlinePresentationSpecs();
if (specs.isEmpty()) {
return inlinePresentation;
}
InlinePresentationSpec specFromHost = specs.get(Math.min(specs.size() - 1, index));
InlinePresentationSpec mergedInlinePresentation = new InlinePresentationSpec.Builder(
inlinePresentation.getInlinePresentationSpec().getMinSize(),
inlinePresentation.getInlinePresentationSpec().getMaxSize()).setStyle(
specFromHost.getStyle()).build();
return new InlinePresentation(inlinePresentation.getSlice(), mergedInlinePresentation,
inlinePresentation.isPinned());
}
// TODO(182306770): creates new class instead of the InlineSuggestion.
private static InlineSuggestion createInlineSuggestionTooltip(
@NonNull InlineSuggestionsRequest request,
@NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
String suggestionSource,
@NonNull InlinePresentation tooltipPresentation) {
if (tooltipPresentation == null) {
return null;
}
final InlinePresentationSpec spec = request.getInlineTooltipPresentationSpec();
InlinePresentationSpec mergedSpec;
if (spec == null) {
mergedSpec = tooltipPresentation.getInlinePresentationSpec();
} else {
mergedSpec = new InlinePresentationSpec.Builder(
tooltipPresentation.getInlinePresentationSpec().getMinSize(),
tooltipPresentation.getInlinePresentationSpec().getMaxSize()).setStyle(
spec.getStyle()).build();
}
InlineFillUi.InlineSuggestionUiCallback uiCallback =
new InlineFillUi.InlineSuggestionUiCallback() {
@Override
public void autofill(Dataset dataset, int datasetIndex) {
/* nothing */
}
@Override
public void authenticate(int requestId, int datasetIndex) {
/* nothing */
}
@Override
public void startIntentSender(IntentSender intentSender) {
/* nothing */
}
@Override
public void onError() {
Slog.w(TAG, "An error happened on the tooltip");
}
};
InlinePresentation tooltipInline = new InlinePresentation(tooltipPresentation.getSlice(),
mergedSpec, false);
IInlineContentProvider tooltipContentProvider = createInlineContentProvider(
inlineFillUiInfo, tooltipInline, () -> { /* no operation */ }, uiCallback);
final InlineSuggestionInfo tooltipInlineSuggestionInfo = new InlineSuggestionInfo(
mergedSpec, suggestionSource, /* autofillHints */ null, TYPE_SUGGESTION,
/* pinned */ false, /* tooltip */ null);
return new InlineSuggestion(tooltipInlineSuggestionInfo, tooltipContentProvider);
}
private static IInlineContentProvider createInlineContentProvider(
@NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
@NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
@NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
RemoteInlineSuggestionViewConnector remoteInlineSuggestionViewConnector =
new RemoteInlineSuggestionViewConnector(inlineFillUiInfo, inlinePresentation,
onClickAction, uiCallback);
return new InlineContentProviderImpl(remoteInlineSuggestionViewConnector, null);
}
private InlineSuggestionFactory() {
}
}