Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bf0d2bc authored by Feng Cao's avatar Feng Cao
Browse files

Add server side implementation for inline augmented autofill request.

Test: manual
Bug: 146453195

Change-Id: Id9c3e16cd5b05ba1c87eb8cdb4a95f2f79bfd77b
parent c1ee9c7c
Loading
Loading
Loading
Loading
+16 −5
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.Dataset;
import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
import android.util.Log;
import android.util.Pair;
@@ -47,6 +48,7 @@ import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAugmentedAutofillManagerClient;
import android.view.autofill.IAutofillWindowPresenter;
import android.view.inputmethod.InlineSuggestionsRequest;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -106,10 +108,11 @@ public abstract class AugmentedAutofillService extends Service {
        @Override
        public void onFillRequest(int sessionId, IBinder client, int taskId,
                ComponentName componentName, AutofillId focusedId, AutofillValue focusedValue,
                long requestTime, IFillCallback callback) {
                long requestTime, @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
                IFillCallback callback) {
            mHandler.sendMessage(obtainMessage(AugmentedAutofillService::handleOnFillRequest,
                    AugmentedAutofillService.this, sessionId, client, taskId, componentName,
                    focusedId, focusedValue, requestTime, callback));
                    focusedId, focusedValue, requestTime, inlineSuggestionsRequest, callback));
        }

        @Override
@@ -212,6 +215,7 @@ public abstract class AugmentedAutofillService extends Service {
    private void handleOnFillRequest(int sessionId, @NonNull IBinder client, int taskId,
            @NonNull ComponentName componentName, @NonNull AutofillId focusedId,
            @Nullable AutofillValue focusedValue, long requestTime,
            @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
            @NonNull IFillCallback callback) {
        if (mAutofillProxies == null) {
            mAutofillProxies = new SparseArray<>();
@@ -236,9 +240,8 @@ public abstract class AugmentedAutofillService extends Service {
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
        // TODO(b/146453195): pass the inline suggestion request over.
        onFillRequest(new FillRequest(proxy, /* inlineSuggestionsRequest= */null),
                cancellationSignal, new FillController(proxy), new FillCallback(proxy));
        onFillRequest(new FillRequest(proxy, inlineSuggestionsRequest), cancellationSignal,
                new FillController(proxy), new FillCallback(proxy));
    }

    private void handleOnDestroyAllFillWindowsRequest() {
@@ -484,6 +487,14 @@ public abstract class AugmentedAutofillService extends Service {
            }
        }

        public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData) {
            try {
                mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}));
            } catch (RemoteException e) {
                Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
            }
        }

        // Used (mostly) for metrics.
        public void report(@ReportEvent int event) {
            if (sVerbose) Log.v(TAG, "report(): " + event);
+10 −1
Original line number Diff line number Diff line
@@ -21,9 +21,12 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.service.autofill.Dataset;
import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.util.Log;

import java.util.List;

/**
 * Callback used to indicate at {@link FillRequest} has been fulfilled.
 *
@@ -55,6 +58,12 @@ public final class FillCallback {
            return;
        }

        List<Dataset> inlineSuggestions = response.getInlineSuggestions();
        if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
            mProxy.onInlineSuggestionsDataReady(inlineSuggestions);
            return;
        }

        final FillWindow fillWindow = response.getFillWindow();
        if (fillWindow != null) {
            fillWindow.show();
+4 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.os.IBinder;
import android.service.autofill.augmented.IFillCallback;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.InlineSuggestionsRequest;

import java.util.List;

@@ -35,7 +36,9 @@ oneway interface IAugmentedAutofillService {
    void onDisconnected();
    void onFillRequest(int sessionId, in IBinder autofillManagerClient, int taskId,
                       in ComponentName activityComponent, in AutofillId focusedId,
                       in AutofillValue focusedValue, long requestTime, in IFillCallback callback);
                       in AutofillValue focusedValue, long requestTime,
                       in InlineSuggestionsRequest inlineSuggestionsRequest,
                       in IFillCallback callback);

    void onDestroyAllFillWindowsRequest();
}
+1 −1
Original line number Diff line number Diff line
@@ -223,7 +223,7 @@ public interface ServiceConnector<I extends IInterface> {
        private final @NonNull ServiceConnection mServiceConnection = this;
        private final @NonNull Runnable mTimeoutDisconnect = this;

        private final @NonNull Context mContext;
        protected final @NonNull Context mContext;
        private final @NonNull Intent mIntent;
        private final int mBindingFlags;
        private final int mUserId;
+135 −0
Original line number Diff line number Diff line
/*
 * 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;

import static com.android.server.autofill.Helper.sDebug;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
import android.service.autofill.Dataset;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.View;
import android.view.autofill.AutofillId;
import android.view.autofill.IAutoFillManagerClient;
import android.view.inline.InlinePresentationSpec;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;

import com.android.internal.view.inline.IInlineContentCallback;
import com.android.internal.view.inline.IInlineContentProvider;
import com.android.server.autofill.ui.InlineSuggestionUi;

import java.util.ArrayList;


/**
 * @hide
 */
public final class InlineSuggestionFactory {
    private static final String TAG = "InlineSuggestionFactory";

    /**
     * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by
     * augmented autofill service.
     */
    public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
            int sessionId,
            @NonNull Dataset[] datasets,
            @NonNull AutofillId autofillId,
            @NonNull InlineSuggestionsRequest request,
            @NonNull Handler uiHandler,
            @NonNull Context context,
            @NonNull IAutoFillManagerClient client) {
        if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");

        final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
        final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context);
        for (Dataset dataset : datasets) {
            // TODO(b/146453195): use the spec in the dataset.
            InlinePresentationSpec spec = request.getPresentationSpecs().get(0);
            if (spec == null) {
                Slog.w(TAG, "InlinePresentationSpec is not provided in the response data set");
                continue;
            }
            InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(sessionId, dataset,
                    autofillId, spec, uiHandler, inlineSuggestionUi, client);
            inlineSuggestions.add(inlineSuggestion);
        }
        return new InlineSuggestionsResponse(inlineSuggestions);
    }

    private static InlineSuggestion createAugmentedInlineSuggestion(int sessionId,
            @NonNull Dataset dataset,
            @NonNull AutofillId autofillId,
            @NonNull InlinePresentationSpec spec,
            @NonNull Handler uiHandler,
            @NonNull InlineSuggestionUi inlineSuggestionUi,
            @NonNull IAutoFillManagerClient client) {
        // TODO(b/146453195): fill in the autofill hint properly.
        final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                spec, InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""});
        final View.OnClickListener onClickListener = createOnClickListener(sessionId, dataset,
                client);
        final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
                createInlineContentProvider(autofillId, dataset, uiHandler, inlineSuggestionUi,
                        onClickListener));
        return inlineSuggestion;
    }

    private static IInlineContentProvider.Stub createInlineContentProvider(
            @NonNull AutofillId autofillId, @NonNull Dataset dataset, @NonNull Handler uiHandler,
            @NonNull InlineSuggestionUi inlineSuggestionUi,
            @Nullable View.OnClickListener onClickListener) {
        return new IInlineContentProvider.Stub() {
            @Override
            public void provideContent(int width, int height,
                    IInlineContentCallback callback) {
                uiHandler.post(() -> {
                    SurfaceControl sc = inlineSuggestionUi.inflate(dataset, autofillId,
                            width, height, onClickListener);
                    try {
                        callback.onContent(sc);
                    } catch (RemoteException e) {
                        Slog.w(TAG, "Encounter exception calling back with inline content.");
                    }
                });
            }
        };
    }

    private static View.OnClickListener createOnClickListener(int sessionId,
            @NonNull Dataset dataset,
            @NonNull IAutoFillManagerClient client) {
        return v -> {
            if (sDebug) Slog.d(TAG, "Inline suggestion clicked");
            try {
                client.autofill(sessionId, dataset.getFieldIds(), dataset.getFieldValues());
            } catch (RemoteException e) {
                Slog.w(TAG, "Encounter exception autofilling the values");
            }
        };
    }

    private InlineSuggestionFactory() {
    }
}
Loading