Merge "AF Inline: Notify the registered AutofillCallback, also fix filtering." into rvc-dev

This commit is contained in:
Ahaan Ugale
2020-05-29 23:03:44 +00:00
committed by Android (Google) Code Review
5 changed files with 112 additions and 28 deletions

View File

@@ -2590,8 +2590,26 @@ public final class AutofillManager {
private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
if (sVerbose) {
Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
+ ", sessionFinishedState=" + sessionFinishedState);
Log.v(TAG, "notifyNoFillUi(): sessionFinishedState=" + sessionFinishedState);
}
final View anchor = findView(id);
if (anchor == null) {
return;
}
notifyCallback(sessionId, id, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
if (sessionFinishedState != STATE_UNKNOWN) {
// Callback call was "hijacked" to also update the session state.
setSessionFinished(sessionFinishedState, /* autofillableIds= */ null);
}
}
private void notifyCallback(
int sessionId, AutofillId id, @AutofillCallback.AutofillEventType int event) {
if (sVerbose) {
Log.v(TAG, "notifyCallback(): sessionId=" + sessionId + ", autofillId=" + id
+ ", event=" + event);
}
final View anchor = findView(id);
if (anchor == null) {
@@ -2607,17 +2625,12 @@ public final class AutofillManager {
if (callback != null) {
if (id.isVirtualInt()) {
callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
AutofillCallback.EVENT_INPUT_UNAVAILABLE);
callback.onAutofillEvent(
anchor, id.getVirtualChildIntId(), event);
} else {
callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
callback.onAutofillEvent(anchor, event);
}
}
if (sessionFinishedState != STATE_UNKNOWN) {
// Callback call was "hijacked" to also update the session state.
setSessionFinished(sessionFinishedState, /* autofillableIds= */ null);
}
}
/**
@@ -3367,6 +3380,26 @@ public final class AutofillManager {
}
}
@Override
public void notifyFillUiShown(int sessionId, AutofillId id) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
afm.post(
() -> afm.notifyCallback(
sessionId, id, AutofillCallback.EVENT_INPUT_SHOWN));
}
}
@Override
public void notifyFillUiHidden(int sessionId, AutofillId id) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
afm.post(
() -> afm.notifyCallback(
sessionId, id, AutofillCallback.EVENT_INPUT_HIDDEN));
}
}
@Override
public void notifyDisableAutofill(long disableDuration, ComponentName componentName)
throws RemoteException {

View File

@@ -78,6 +78,18 @@ oneway interface IAutoFillManagerClient {
*/
void notifyNoFillUi(int sessionId, in AutofillId id, int sessionFinishedState);
/**
* Notifies that the fill UI was shown by the system (e.g. as inline chips in the keyboard).
*/
void notifyFillUiShown(int sessionId, in AutofillId id);
/**
* Notifies that the fill UI previously shown by the system has been hidden by the system.
*
* @see #notifyFillUiShown
*/
void notifyFillUiHidden(int sessionId, in AutofillId id);
/**
* Dispatches unhandled keyevent from autofill ui. Autofill ui handles DPAD and ENTER events,
* other unhandled keyevents are dispatched to app's window to filter autofill result.

View File

@@ -117,13 +117,14 @@ final class AutofillInlineSessionController {
}
/**
* Permanently delete the current inline fill UI. Notify the IME to hide the suggestions as
* well.
* Disables prefix/regex based filtering. Other filtering rules (see {@link
* android.service.autofill.Dataset}) still apply.
*/
@GuardedBy("mLock")
boolean deleteInlineFillUiLocked(@NonNull AutofillId autofillId) {
mInlineFillUi = null;
return hideInlineSuggestionsUiLocked(autofillId);
void disableFilterMatching(@NonNull AutofillId autofillId) {
if (mInlineFillUi != null && mInlineFillUi.getAutofillId().equals(autofillId)) {
mInlineFillUi.disableFilterMatching();
}
}
/**

View File

@@ -2698,6 +2698,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// TODO(b/156099633): remove this once framework gets out of business of resending
// inline suggestions when IME visibility changes.
mInlineSessionController.hideInlineSuggestionsUiLocked(viewState.id);
try {
mClient.notifyFillUiHidden(this.id, viewState.id);
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting to hide fill UI", e);
}
viewState.resetState(ViewState.STATE_CHANGED);
return;
} else if ((viewState.id.equals(this.mCurrentViewId))
@@ -2713,20 +2718,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} else if (viewState.id.equals(this.mCurrentViewId)
&& (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) {
if ((viewState.getState() & ViewState.STATE_INLINE_DISABLED) != 0) {
final FillResponse response = viewState.getResponse();
if (response != null) {
response.getDatasets().clear();
}
mInlineSessionController.deleteInlineFillUiLocked(viewState.id);
} else {
mInlineSessionController.filterInlineFillUiLocked(mCurrentViewId, filterText);
mInlineSessionController.disableFilterMatching(viewState.id);
}
mInlineSessionController.filterInlineFillUiLocked(mCurrentViewId, filterText);
} else if (viewState.id.equals(this.mCurrentViewId)
&& (viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
if (!TextUtils.isEmpty(filterText)) {
// TODO: we should be able to replace this with controller#filterInlineFillUiLocked
// to accomplish filtering for augmented autofill.
mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
try {
mClient.notifyFillUiHidden(this.id, mCurrentViewId);
} catch (RemoteException e) {
Slog.e(TAG, "Error sending fill UI hidden notification", e);
}
}
}
@@ -2812,6 +2817,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (requestShowInlineSuggestionsLocked(response, filterText)) {
final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_INLINE_SHOWN);
try {
mClient.notifyFillUiShown(this.id, mCurrentViewId);
} catch (RemoteException e) {
Slog.e(TAG, "Error sending fill UI shown notification", e);
}
//TODO(b/137800469): Fix it to log showed only when IME asks for inflation,
// rather than here where framework sends back the response.
mService.logDatasetShown(id, mClientState);
@@ -2882,6 +2892,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
synchronized (mLock) {
mInlineSessionController.hideInlineSuggestionsUiLocked(
focusedId);
try {
mClient.notifyFillUiHidden(this.id, focusedId);
} catch (RemoteException e) {
Slog.e(TAG, "Error sending fill UI hidden notification", e);
}
}
}, remoteRenderService);
return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
@@ -3393,6 +3408,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (mCurrentViewId != null) {
mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
try {
mClient.notifyFillUiHidden(this.id, mCurrentViewId);
} catch (RemoteException e) {
Slog.e(TAG, "Error sending fill UI hidden notification", e);
}
}
autoFillApp(dataset);
return;

View File

@@ -83,6 +83,11 @@ public final class InlineFillUi {
@Nullable
private String mFilterText;
/**
* Whether prefix/regex based filtering is disabled.
*/
private boolean mFilterMatchingDisabled;
/**
* Returns an empty inline autofill UI.
*/
@@ -199,7 +204,7 @@ public final class InlineFillUi {
continue;
}
if (!inlinePresentation.isPinned() // don't filter pinned suggestions
&& !includeDataset(dataset, fieldIndex, mFilterText)) {
&& !includeDataset(dataset, fieldIndex)) {
continue;
}
inlineSuggestions.add(copy(i, mInlineSuggestions.get(i)));
@@ -235,14 +240,13 @@ public final class InlineFillUi {
}
// TODO: Extract the shared filtering logic here and in FillUi to a common method.
private static boolean includeDataset(Dataset dataset, int fieldIndex,
@Nullable String filterText) {
private boolean includeDataset(Dataset dataset, int fieldIndex) {
// Show everything when the user input is empty.
if (TextUtils.isEmpty(filterText)) {
if (TextUtils.isEmpty(mFilterText)) {
return true;
}
final String constraintLowerCase = filterText.toString().toLowerCase();
final String constraintLowerCase = mFilterText.toString().toLowerCase();
// Use the filter provided by the service, if available.
final Dataset.DatasetFieldFilter filter = dataset.getFilter(fieldIndex);
@@ -252,7 +256,10 @@ public final class InlineFillUi {
if (sVerbose) {
Slog.v(TAG, "Explicitly disabling filter for dataset id" + dataset.getId());
}
return true;
return false;
}
if (mFilterMatchingDisabled) {
return false;
}
return filterPattern.matcher(constraintLowerCase).matches();
}
@@ -261,10 +268,21 @@ public final class InlineFillUi {
if (value == null || !value.isText()) {
return dataset.getAuthentication() == null;
}
if (mFilterMatchingDisabled) {
return false;
}
final String valueText = value.getTextValue().toString().toLowerCase();
return valueText.toLowerCase().startsWith(constraintLowerCase);
}
/**
* Disables prefix/regex based filtering. Other filtering rules (see {@link
* android.service.autofill.Dataset}) still apply.
*/
public void disableFilterMatching() {
mFilterMatchingDisabled = true;
}
/**
* Callback from the inline suggestion Ui.
*/