Merge "Several improvements and bug fixes to the inline autofill flow" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0fe12dad61
@@ -27,6 +27,7 @@ import android.os.IBinder;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.autofill.AutofillId;
|
||||||
import android.view.inputmethod.InlineSuggestionsRequest;
|
import android.view.inputmethod.InlineSuggestionsRequest;
|
||||||
import android.view.inputmethod.InlineSuggestionsResponse;
|
import android.view.inputmethod.InlineSuggestionsResponse;
|
||||||
|
|
||||||
@@ -145,8 +146,8 @@ class InlineSuggestionSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
|
public void onInlineSuggestionsResponse(AutofillId fieldId,
|
||||||
throws RemoteException {
|
InlineSuggestionsResponse response) {
|
||||||
final InlineSuggestionSession session = mInlineSuggestionSession.get();
|
final InlineSuggestionSession session = mInlineSuggestionSession.get();
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
session.mHandler.sendMessage(obtainMessage(
|
session.mHandler.sendMessage(obtainMessage(
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import android.os.RemoteException;
|
|||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.service.autofill.Dataset;
|
import android.service.autofill.Dataset;
|
||||||
import android.service.autofill.FillEventHistory;
|
import android.service.autofill.FillEventHistory;
|
||||||
|
import android.service.autofill.InlinePresentation;
|
||||||
import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
|
import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
@@ -557,12 +558,10 @@ public abstract class AugmentedAutofillService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reportResult(@Nullable List<Dataset> inlineSuggestionsData) {
|
void reportResult(@Nullable List<Dataset> inlineSuggestionsData,
|
||||||
|
@Nullable List<InlinePresentation> inlineActions) {
|
||||||
try {
|
try {
|
||||||
final Dataset[] inlineSuggestions = (inlineSuggestionsData != null)
|
mCallback.onSuccess(inlineSuggestionsData, inlineActions);
|
||||||
? inlineSuggestionsData.toArray(new Dataset[inlineSuggestionsData.size()])
|
|
||||||
: null;
|
|
||||||
mCallback.onSuccess(inlineSuggestions);
|
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
|
Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,14 +55,14 @@ public final class FillCallback {
|
|||||||
|
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
mProxy.logEvent(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
|
mProxy.logEvent(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
|
||||||
mProxy.reportResult(null /*inlineSuggestions*/);
|
mProxy.reportResult(/* inlineSuggestionsData */ null, /* inlineActions */null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Dataset> inlineSuggestions = response.getInlineSuggestions();
|
List<Dataset> inlineSuggestions = response.getInlineSuggestions();
|
||||||
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
|
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
|
||||||
mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
|
mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
|
||||||
mProxy.reportResult(inlineSuggestions);
|
mProxy.reportResult(inlineSuggestions, response.getInlineActions());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ import android.os.Bundle;
|
|||||||
import android.os.ICancellationSignal;
|
import android.os.ICancellationSignal;
|
||||||
|
|
||||||
import android.service.autofill.Dataset;
|
import android.service.autofill.Dataset;
|
||||||
|
import android.service.autofill.InlinePresentation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to receive the result of an autofill request.
|
* Interface to receive the result of an autofill request.
|
||||||
@@ -28,7 +31,8 @@ import android.service.autofill.Dataset;
|
|||||||
*/
|
*/
|
||||||
interface IFillCallback {
|
interface IFillCallback {
|
||||||
void onCancellable(in ICancellationSignal cancellation);
|
void onCancellable(in ICancellationSignal cancellation);
|
||||||
void onSuccess(in @nullable Dataset[] inlineSuggestionsData);
|
void onSuccess(in @nullable List<Dataset> inlineSuggestionsData,
|
||||||
|
in @nullable List<InlinePresentation> inlineActions);
|
||||||
boolean isCompleted();
|
boolean isCompleted();
|
||||||
void cancel();
|
void cancel();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.internal.view;
|
package com.android.internal.view;
|
||||||
|
|
||||||
|
import android.view.autofill.AutofillId;
|
||||||
import android.view.inputmethod.InlineSuggestionsResponse;
|
import android.view.inputmethod.InlineSuggestionsResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,5 +24,5 @@ import android.view.inputmethod.InlineSuggestionsResponse;
|
|||||||
* {@hide}
|
* {@hide}
|
||||||
*/
|
*/
|
||||||
oneway interface IInlineSuggestionsResponseCallback {
|
oneway interface IInlineSuggestionsResponseCallback {
|
||||||
void onInlineSuggestionsResponse(in InlineSuggestionsResponse response);
|
void onInlineSuggestionsResponse(in AutofillId fieldId, in InlineSuggestionsResponse response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,10 @@ import android.content.ComponentName;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Slog;
|
||||||
import android.view.autofill.AutofillId;
|
import android.view.autofill.AutofillId;
|
||||||
import android.view.inputmethod.InlineSuggestionsRequest;
|
import android.view.inputmethod.InlineSuggestionsRequest;
|
||||||
|
import android.view.inputmethod.InlineSuggestionsResponse;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.view.IInlineSuggestionsRequestCallback;
|
import com.android.internal.view.IInlineSuggestionsRequestCallback;
|
||||||
@@ -33,6 +35,8 @@ import com.android.internal.view.IInlineSuggestionsResponseCallback;
|
|||||||
import com.android.internal.view.InlineSuggestionsRequestInfo;
|
import com.android.internal.view.InlineSuggestionsRequestInfo;
|
||||||
import com.android.server.inputmethod.InputMethodManagerInternal;
|
import com.android.server.inputmethod.InputMethodManagerInternal;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
@@ -40,9 +44,16 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maintains an inline suggestion autofill session.
|
* Maintains an autofill inline suggestion session that communicates with the IME.
|
||||||
*
|
*
|
||||||
* <p> This class is thread safe.
|
* <p>
|
||||||
|
* The same session may be reused for multiple input fields involved in the same autofill
|
||||||
|
* {@link Session}. Therefore, one {@link InlineSuggestionsRequest} and one
|
||||||
|
* {@link IInlineSuggestionsResponseCallback} may be used to generate and callback with inline
|
||||||
|
* suggestions for different input fields.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class is thread safe.
|
||||||
*/
|
*/
|
||||||
final class InlineSuggestionSession {
|
final class InlineSuggestionSession {
|
||||||
|
|
||||||
@@ -61,6 +72,9 @@ final class InlineSuggestionSession {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private CompletableFuture<ImeResponse> mPendingImeResponse;
|
private CompletableFuture<ImeResponse> mPendingImeResponse;
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private boolean mIsLastResponseNonEmpty = false;
|
||||||
|
|
||||||
InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
|
InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
|
||||||
int userId, ComponentName componentName) {
|
int userId, ComponentName componentName) {
|
||||||
mInputMethodManagerInternal = inputMethodManagerInternal;
|
mInputMethodManagerInternal = inputMethodManagerInternal;
|
||||||
@@ -69,26 +83,28 @@ final class InlineSuggestionSession {
|
|||||||
mLock = new Object();
|
mLock = new Object();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createRequest(@NonNull AutofillId currentViewId) {
|
public void onCreateInlineSuggestionsRequest(@NonNull AutofillId autofillId) {
|
||||||
|
if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequest called for " + autofillId);
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
cancelCurrentRequest();
|
cancelCurrentRequest();
|
||||||
mPendingImeResponse = new CompletableFuture<>();
|
mPendingImeResponse = new CompletableFuture<>();
|
||||||
// TODO(b/146454892): pipe the uiExtras from the ExtServices.
|
// TODO(b/146454892): pipe the uiExtras from the ExtServices.
|
||||||
mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
|
mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
|
||||||
mUserId,
|
mUserId,
|
||||||
new InlineSuggestionsRequestInfo(mComponentName, currentViewId, new Bundle()),
|
new InlineSuggestionsRequestInfo(mComponentName, autofillId, new Bundle()),
|
||||||
new InlineSuggestionsRequestCallbackImpl(mPendingImeResponse));
|
new InlineSuggestionsRequestCallbackImpl(mPendingImeResponse));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public Optional<InlineSuggestionsRequest> waitAndGetInlineSuggestionsRequest() {
|
||||||
public ImeResponse waitAndGetImeResponse() {
|
final CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
|
||||||
CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
|
if (pendingImeResponse == null) {
|
||||||
if (pendingImeResponse == null || pendingImeResponse.isCancelled()) {
|
return Optional.empty();
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return pendingImeResponse.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
return Optional.ofNullable(pendingImeResponse.get(INLINE_REQUEST_TIMEOUT_MS,
|
||||||
|
TimeUnit.MILLISECONDS)).map(ImeResponse::getRequest);
|
||||||
} catch (TimeoutException e) {
|
} catch (TimeoutException e) {
|
||||||
Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
|
Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
|
||||||
} catch (CancellationException e) {
|
} catch (CancellationException e) {
|
||||||
@@ -96,13 +112,59 @@ final class InlineSuggestionSession {
|
|||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
return null;
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hideInlineSuggestionsUi(@NonNull AutofillId autofillId) {
|
||||||
|
if (sDebug) Log.d(TAG, "Called hideInlineSuggestionsUi for " + autofillId);
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mIsLastResponseNonEmpty) {
|
||||||
|
if (sDebug) Log.d(TAG, "Send empty suggestion to IME");
|
||||||
|
return onInlineSuggestionsResponseLocked(autofillId,
|
||||||
|
new InlineSuggestionsResponse(Collections.EMPTY_LIST));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onInlineSuggestionsResponse(@NonNull AutofillId autofillId,
|
||||||
|
@NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
return onInlineSuggestionsResponseLocked(autofillId, inlineSuggestionsResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
|
||||||
|
@NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
|
||||||
|
final CompletableFuture<ImeResponse> completedImsResponse = getPendingImeResponse();
|
||||||
|
if (completedImsResponse == null || !completedImsResponse.isDone()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// There is no need to wait on the CompletableFuture since it should have been completed
|
||||||
|
// when {@link #waitAndGetInlineSuggestionsRequest()} was called.
|
||||||
|
ImeResponse imeResponse = completedImsResponse.getNow(null);
|
||||||
|
if (imeResponse == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
imeResponse.mCallback.onInlineSuggestionsResponse(autofillId,
|
||||||
|
inlineSuggestionsResponse);
|
||||||
|
mIsLastResponseNonEmpty = !inlineSuggestionsResponse.getInlineSuggestions().isEmpty();
|
||||||
|
if (sDebug) {
|
||||||
|
Log.d(TAG, "Autofill sends inline response to IME: "
|
||||||
|
+ inlineSuggestionsResponse.getInlineSuggestions().size());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Slog.e(TAG, "RemoteException sending InlineSuggestionsResponse to IME");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cancelCurrentRequest() {
|
private void cancelCurrentRequest() {
|
||||||
CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
|
CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
|
||||||
if (pendingImeResponse != null) {
|
if (pendingImeResponse != null && !pendingImeResponse.isDone()) {
|
||||||
pendingImeResponse.cancel(true);
|
pendingImeResponse.complete(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,22 +187,18 @@ final class InlineSuggestionSession {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInlineSuggestionsUnsupported() throws RemoteException {
|
public void onInlineSuggestionsUnsupported() throws RemoteException {
|
||||||
if (sDebug) {
|
if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
|
||||||
Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
|
mResponse.complete(null);
|
||||||
}
|
|
||||||
mResponse.cancel(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
|
public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
|
||||||
IInlineSuggestionsResponseCallback callback) throws RemoteException {
|
IInlineSuggestionsResponseCallback callback) {
|
||||||
if (sDebug) {
|
if (sDebug) Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
|
||||||
Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
|
|
||||||
}
|
|
||||||
if (request != null && callback != null) {
|
if (request != null && callback != null) {
|
||||||
mResponse.complete(new ImeResponse(request, callback));
|
mResponse.complete(new ImeResponse(request, callback));
|
||||||
} else {
|
} else {
|
||||||
mResponse.cancel(true);
|
mResponse.complete(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,12 +206,12 @@ final class InlineSuggestionSession {
|
|||||||
/**
|
/**
|
||||||
* A data class wrapping IME responses for the inline suggestion request.
|
* A data class wrapping IME responses for the inline suggestion request.
|
||||||
*/
|
*/
|
||||||
public static class ImeResponse {
|
private static class ImeResponse {
|
||||||
@NonNull
|
@NonNull
|
||||||
private final InlineSuggestionsRequest mRequest;
|
final InlineSuggestionsRequest mRequest;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final IInlineSuggestionsResponseCallback mCallback;
|
final IInlineSuggestionsResponseCallback mCallback;
|
||||||
|
|
||||||
ImeResponse(@NonNull InlineSuggestionsRequest request,
|
ImeResponse(@NonNull InlineSuggestionsRequest request,
|
||||||
@NonNull IInlineSuggestionsResponseCallback callback) {
|
@NonNull IInlineSuggestionsResponseCallback callback) {
|
||||||
@@ -161,12 +219,8 @@ final class InlineSuggestionSession {
|
|||||||
mCallback = callback;
|
mCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InlineSuggestionsRequest getRequest() {
|
InlineSuggestionsRequest getRequest() {
|
||||||
return mRequest;
|
return mRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IInlineSuggestionsResponseCallback getCallback() {
|
|
||||||
return mCallback;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import android.os.ICancellationSignal;
|
|||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.service.autofill.Dataset;
|
import android.service.autofill.Dataset;
|
||||||
|
import android.service.autofill.InlinePresentation;
|
||||||
import android.service.autofill.augmented.AugmentedAutofillService;
|
import android.service.autofill.augmented.AugmentedAutofillService;
|
||||||
import android.service.autofill.augmented.IAugmentedAutofillService;
|
import android.service.autofill.augmented.IAugmentedAutofillService;
|
||||||
import android.service.autofill.augmented.IFillCallback;
|
import android.service.autofill.augmented.IFillCallback;
|
||||||
@@ -54,14 +55,14 @@ import com.android.internal.infra.AndroidFuture;
|
|||||||
import com.android.internal.infra.ServiceConnector;
|
import com.android.internal.infra.ServiceConnector;
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
import com.android.internal.os.IResultReceiver;
|
import com.android.internal.os.IResultReceiver;
|
||||||
import com.android.internal.util.ArrayUtils;
|
|
||||||
import com.android.internal.view.IInlineSuggestionsResponseCallback;
|
|
||||||
import com.android.server.autofill.ui.InlineSuggestionFactory;
|
import com.android.server.autofill.ui.InlineSuggestionFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
final class RemoteAugmentedAutofillService
|
final class RemoteAugmentedAutofillService
|
||||||
extends ServiceConnector.Impl<IAugmentedAutofillService> {
|
extends ServiceConnector.Impl<IAugmentedAutofillService> {
|
||||||
@@ -146,7 +147,7 @@ final class RemoteAugmentedAutofillService
|
|||||||
int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
|
int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
|
||||||
@Nullable AutofillValue focusedValue,
|
@Nullable AutofillValue focusedValue,
|
||||||
@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
|
@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
|
||||||
@Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
|
@Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback,
|
||||||
@NonNull Runnable onErrorCallback,
|
@NonNull Runnable onErrorCallback,
|
||||||
@NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
|
@NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
|
||||||
long requestTime = SystemClock.elapsedRealtime();
|
long requestTime = SystemClock.elapsedRealtime();
|
||||||
@@ -165,12 +166,13 @@ final class RemoteAugmentedAutofillService
|
|||||||
focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
|
focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
|
||||||
new IFillCallback.Stub() {
|
new IFillCallback.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) {
|
public void onSuccess(@Nullable List<Dataset> inlineSuggestionsData,
|
||||||
|
@Nullable List<InlinePresentation> inlineActions) {
|
||||||
mCallbacks.resetLastResponse();
|
mCallbacks.resetLastResponse();
|
||||||
maybeRequestShowInlineSuggestions(sessionId,
|
maybeRequestShowInlineSuggestions(sessionId,
|
||||||
inlineSuggestionsRequest, inlineSuggestionsData,
|
inlineSuggestionsRequest, inlineSuggestionsData,
|
||||||
focusedId, inlineSuggestionsCallback, client,
|
inlineActions, focusedId, inlineSuggestionsCallback,
|
||||||
onErrorCallback, remoteRenderService);
|
client, onErrorCallback, remoteRenderService);
|
||||||
requestAutofill.complete(null);
|
requestAutofill.complete(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,20 +235,21 @@ final class RemoteAugmentedAutofillService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void maybeRequestShowInlineSuggestions(int sessionId,
|
private void maybeRequestShowInlineSuggestions(int sessionId,
|
||||||
@Nullable InlineSuggestionsRequest request, @Nullable Dataset[] inlineSuggestionsData,
|
@Nullable InlineSuggestionsRequest request,
|
||||||
@NonNull AutofillId focusedId,
|
@Nullable List<Dataset> inlineSuggestionsData,
|
||||||
@Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
|
@Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId focusedId,
|
||||||
|
@Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback,
|
||||||
@NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
|
@NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
|
||||||
@NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
|
@NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
|
||||||
if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null
|
if (inlineSuggestionsData == null || inlineSuggestionsData.isEmpty()
|
||||||
|| request == null) {
|
|| inlineSuggestionsCallback == null || request == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mCallbacks.setLastResponse(sessionId);
|
mCallbacks.setLastResponse(sessionId);
|
||||||
|
|
||||||
final InlineSuggestionsResponse inlineSuggestionsResponse =
|
final InlineSuggestionsResponse inlineSuggestionsResponse =
|
||||||
InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
|
InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
|
||||||
request, inlineSuggestionsData, focusedId, mContext,
|
request, inlineSuggestionsData, inlineActions, focusedId, mContext,
|
||||||
dataset -> {
|
dataset -> {
|
||||||
mCallbacks.logAugmentedAutofillSelected(sessionId,
|
mCallbacks.logAugmentedAutofillSelected(sessionId,
|
||||||
dataset.getId());
|
dataset.getId());
|
||||||
@@ -262,15 +265,10 @@ final class RemoteAugmentedAutofillService
|
|||||||
Slog.w(TAG, "InlineSuggestionFactory created null response");
|
Slog.w(TAG, "InlineSuggestionFactory created null response");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (inlineSuggestionsCallback.apply(inlineSuggestionsResponse)) {
|
||||||
try {
|
|
||||||
inlineSuggestionsCallback.onInlineSuggestionsResponse(inlineSuggestionsResponse);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
|
|
||||||
}
|
|
||||||
|
|
||||||
mCallbacks.logAugmentedAutofillShown(sessionId);
|
mCallbacks.logAugmentedAutofillShown(sessionId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ import com.android.internal.annotations.GuardedBy;
|
|||||||
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.MetricsLogger;
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.internal.view.IInlineSuggestionsResponseCallback;
|
|
||||||
import com.android.server.autofill.ui.AutoFillUI;
|
import com.android.server.autofill.ui.AutoFillUI;
|
||||||
import com.android.server.autofill.ui.InlineSuggestionFactory;
|
import com.android.server.autofill.ui.InlineSuggestionFactory;
|
||||||
import com.android.server.autofill.ui.PendingUi;
|
import com.android.server.autofill.ui.PendingUi;
|
||||||
@@ -113,6 +112,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -403,11 +403,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
|
|||||||
final ArrayList<FillContext> contexts =
|
final ArrayList<FillContext> contexts =
|
||||||
mergePreviousSessionLocked(/* forSave= */ false);
|
mergePreviousSessionLocked(/* forSave= */ false);
|
||||||
|
|
||||||
final InlineSuggestionSession.ImeResponse imeResponse =
|
final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
|
||||||
mInlineSuggestionSession.waitAndGetImeResponse();
|
mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
|
||||||
|
|
||||||
request = new FillRequest(requestId, contexts, mClientState, flags,
|
request = new FillRequest(requestId, contexts, mClientState, flags,
|
||||||
imeResponse != null ? imeResponse.getRequest() : null);
|
inlineSuggestionsRequest.orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mActivityToken != null) {
|
if (mActivityToken != null) {
|
||||||
@@ -605,7 +604,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
|
|||||||
private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState,
|
private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState,
|
||||||
int newState, int flags) {
|
int newState, int flags) {
|
||||||
if (isInlineSuggestionsEnabled()) {
|
if (isInlineSuggestionsEnabled()) {
|
||||||
mInlineSuggestionSession.createRequest(mCurrentViewId);
|
mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestNewFillResponseLocked(viewState, newState, flags);
|
requestNewFillResponseLocked(viewState, newState, flags);
|
||||||
@@ -1158,18 +1157,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
|
|||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Slog.e(TAG, "Error requesting to hide fill UI", e);
|
Slog.e(TAG, "Error requesting to hide fill UI", e);
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
final InlineSuggestionSession.ImeResponse imeResponse =
|
|
||||||
mInlineSuggestionSession.waitAndGetImeResponse();
|
|
||||||
if (imeResponse == null) {
|
|
||||||
Log.w(TAG, "Session input method callback is not set yet");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
imeResponse.getCallback().onInlineSuggestionsResponse(
|
|
||||||
new InlineSuggestionsResponse(Collections.EMPTY_LIST));
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Slog.e(TAG, "RemoteException hiding inline suggestions");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2501,6 +2488,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
|
|||||||
if (sVerbose) Slog.v(TAG, "Exiting view " + id);
|
if (sVerbose) Slog.v(TAG, "Exiting view " + id);
|
||||||
mUi.hideFillUi(this);
|
mUi.hideFillUi(this);
|
||||||
hideAugmentedAutofillLocked(viewState);
|
hideAugmentedAutofillLocked(viewState);
|
||||||
|
mInlineSuggestionSession.hideInlineSuggestionsUi(mCurrentViewId);
|
||||||
mCurrentViewId = null;
|
mCurrentViewId = null;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2668,38 +2656,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
|
|||||||
*/
|
*/
|
||||||
private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
|
private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
|
||||||
@Nullable String filterText) {
|
@Nullable String filterText) {
|
||||||
final List<Dataset> datasets = response.getDatasets();
|
final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
|
||||||
|
mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
|
||||||
final InlineSuggestionSession.ImeResponse imeResponse =
|
if (!inlineSuggestionsRequest.isPresent()) {
|
||||||
mInlineSuggestionSession.waitAndGetImeResponse();
|
Log.w(TAG, "InlineSuggestionsRequest unavailable");
|
||||||
if (imeResponse == null) {
|
|
||||||
Log.w(TAG, "Session input method callback is not set yet");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final InlineSuggestionsRequest request = imeResponse.getRequest();
|
|
||||||
InlineSuggestionsResponse inlineSuggestionsResponse =
|
InlineSuggestionsResponse inlineSuggestionsResponse =
|
||||||
InlineSuggestionFactory.createInlineSuggestionsResponse(request,
|
InlineSuggestionFactory.createInlineSuggestionsResponse(
|
||||||
|
inlineSuggestionsRequest.get(),
|
||||||
response, filterText, response.getInlineActions(), mCurrentViewId, mContext,
|
response, filterText, response.getInlineActions(), mCurrentViewId, mContext,
|
||||||
this, () -> {
|
this, () -> {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
requestHideFillUi(mCurrentViewId);
|
requestHideFillUi(mCurrentViewId);
|
||||||
}
|
}
|
||||||
}, mService.getRemoteInlineSuggestionRenderServiceLocked());
|
}, mService.getRemoteInlineSuggestionRenderServiceLocked());
|
||||||
|
|
||||||
if (inlineSuggestionsResponse == null) {
|
if (inlineSuggestionsResponse == null) {
|
||||||
Slog.w(TAG, "InlineSuggestionFactory created null response");
|
Slog.w(TAG, "InlineSuggestionFactory created null response");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return mInlineSuggestionSession.onInlineSuggestionsResponse(mCurrentViewId,
|
||||||
try {
|
inlineSuggestionsResponse);
|
||||||
imeResponse.getCallback().onInlineSuggestionsResponse(inlineSuggestionsResponse);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.w(TAG, "onFillReady() remote error calling onInlineSuggestionsResponse()");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isDestroyed() {
|
boolean isDestroyed() {
|
||||||
@@ -2982,16 +2959,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
|
|||||||
// doesn't want autofill
|
// doesn't want autofill
|
||||||
if (mForAugmentedAutofillOnly || !isInlineSuggestionsEnabled()) {
|
if (mForAugmentedAutofillOnly || !isInlineSuggestionsEnabled()) {
|
||||||
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
|
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
|
||||||
mInlineSuggestionSession.createRequest(mCurrentViewId);
|
mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId);
|
||||||
}
|
}
|
||||||
InlineSuggestionSession.ImeResponse imeResponse =
|
|
||||||
mInlineSuggestionSession.waitAndGetImeResponse();
|
Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
|
||||||
final InlineSuggestionsRequest inlineSuggestionsRequest =
|
mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
|
||||||
imeResponse != null ? imeResponse.getRequest() : null;
|
|
||||||
final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
|
|
||||||
imeResponse != null ? imeResponse.getCallback() : null;
|
|
||||||
remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
|
remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
|
||||||
currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback, () -> {
|
currentValue, inlineSuggestionsRequest.orElse(null),
|
||||||
|
response -> mInlineSuggestionSession.onInlineSuggestionsResponse(
|
||||||
|
mCurrentViewId, response),
|
||||||
|
() -> {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
cancelAugmentedAutofillLocked();
|
cancelAugmentedAutofillLocked();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ public final class InlineSuggestionFactory {
|
|||||||
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
|
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
|
||||||
* autofill service, potentially filtering the datasets.
|
* autofill service, potentially filtering the datasets.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public static InlineSuggestionsResponse createInlineSuggestionsResponse(
|
public static InlineSuggestionsResponse createInlineSuggestionsResponse(
|
||||||
@NonNull InlineSuggestionsRequest request, @NonNull FillResponse response,
|
@NonNull InlineSuggestionsRequest request, @NonNull FillResponse response,
|
||||||
@Nullable String filterText, @Nullable List<InlinePresentation> inlineActions,
|
@Nullable String filterText, @Nullable List<InlinePresentation> inlineActions,
|
||||||
@@ -92,23 +93,21 @@ public final class InlineSuggestionFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Dataset> datasetList = response.getDatasets();
|
|
||||||
final Dataset[] datasets = datasetList == null
|
|
||||||
? null
|
|
||||||
: datasetList.toArray(new Dataset[]{});
|
|
||||||
final InlinePresentation inlineAuthentication =
|
final InlinePresentation inlineAuthentication =
|
||||||
response.getAuthentication() == null ? null : response.getInlinePresentation();
|
response.getAuthentication() == null ? null : response.getInlinePresentation();
|
||||||
return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request,
|
return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request,
|
||||||
datasets, filterText, inlineAuthentication, inlineActions, autofillId, context,
|
response.getDatasets(), filterText, inlineAuthentication, inlineActions, autofillId,
|
||||||
onErrorCallback, onClickFactory, remoteRenderService);
|
context, onErrorCallback, onClickFactory, remoteRenderService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by augmented
|
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by augmented
|
||||||
* autofill service.
|
* autofill service.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
|
public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
|
||||||
@NonNull InlineSuggestionsRequest request, @NonNull Dataset[] datasets,
|
@NonNull InlineSuggestionsRequest request, @NonNull List<Dataset> datasets,
|
||||||
|
@Nullable List<InlinePresentation> inlineActions,
|
||||||
@NonNull AutofillId autofillId, @NonNull Context context,
|
@NonNull AutofillId autofillId, @NonNull Context context,
|
||||||
@NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
|
@NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
|
||||||
@NonNull Runnable onErrorCallback,
|
@NonNull Runnable onErrorCallback,
|
||||||
@@ -116,14 +115,15 @@ public final class InlineSuggestionFactory {
|
|||||||
if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
|
if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
|
||||||
return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
|
return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
|
||||||
datasets, /* filterText= */ null, /* inlineAuthentication= */ null,
|
datasets, /* filterText= */ null, /* inlineAuthentication= */ null,
|
||||||
/* inlineActions= */ null, autofillId, context, onErrorCallback,
|
inlineActions, autofillId, context, onErrorCallback,
|
||||||
(dataset, datasetIndex) ->
|
(dataset, datasetIndex) ->
|
||||||
inlineSuggestionUiCallback.autofill(dataset), remoteRenderService);
|
inlineSuggestionUiCallback.autofill(dataset), remoteRenderService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
|
private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
|
||||||
boolean isAugmented, @NonNull InlineSuggestionsRequest request,
|
boolean isAugmented, @NonNull InlineSuggestionsRequest request,
|
||||||
@Nullable Dataset[] datasets, @Nullable String filterText,
|
@Nullable List<Dataset> datasets, @Nullable String filterText,
|
||||||
@Nullable InlinePresentation inlineAuthentication,
|
@Nullable InlinePresentation inlineAuthentication,
|
||||||
@Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId,
|
@Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId,
|
||||||
@NonNull Context context, @NonNull Runnable onErrorCallback,
|
@NonNull Context context, @NonNull Runnable onErrorCallback,
|
||||||
@@ -145,8 +145,8 @@ public final class InlineSuggestionFactory {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
|
for (int datasetIndex = 0; datasetIndex < datasets.size(); datasetIndex++) {
|
||||||
final Dataset dataset = datasets[datasetIndex];
|
final Dataset dataset = datasets.get(datasetIndex);
|
||||||
final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
|
final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
|
||||||
if (fieldIndex < 0) {
|
if (fieldIndex < 0) {
|
||||||
Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
|
Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
|
||||||
@@ -169,7 +169,8 @@ public final class InlineSuggestionFactory {
|
|||||||
|
|
||||||
inlineSuggestions.add(inlineSuggestion);
|
inlineSuggestions.add(inlineSuggestion);
|
||||||
}
|
}
|
||||||
if (inlineActions != null) {
|
// We should only add inline actions if there is at least one suggestion.
|
||||||
|
if (!inlineSuggestions.isEmpty() && inlineActions != null) {
|
||||||
for (InlinePresentation inlinePresentation : inlineActions) {
|
for (InlinePresentation inlinePresentation : inlineActions) {
|
||||||
final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
|
final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
|
||||||
mergedInlinePresentation(request, 0, inlinePresentation),
|
mergedInlinePresentation(request, 0, inlinePresentation),
|
||||||
|
|||||||
Reference in New Issue
Block a user