Merge "Scrollable inline keyboard suggestions"

This commit is contained in:
Svetoslav Ganov
2020-02-05 18:52:35 +00:00
committed by Android (Google) Code Review
17 changed files with 304 additions and 142 deletions

View File

@@ -31,7 +31,6 @@ import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
@@ -93,7 +92,7 @@ public abstract class AugmentedAutofillService extends Service {
// Used for metrics / debug only
private ComponentName mServiceComponentName;
private final IAugmentedAutofillService mInterface = new IAugmentedAutofillService.Stub() {
private final class AugmentedAutofillServiceImpl extends IAugmentedAutofillService.Stub {
@Override
public void onConnected(boolean debug, boolean verbose) {
@@ -137,7 +136,7 @@ public abstract class AugmentedAutofillService extends Service {
public final IBinder onBind(Intent intent) {
mServiceComponentName = intent.getComponent();
if (SERVICE_INTERFACE.equals(intent.getAction())) {
return mInterface.asBinder();
return new AugmentedAutofillServiceImpl();
}
Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
return null;
@@ -352,11 +351,13 @@ public abstract class AugmentedAutofillService extends Service {
static final int REPORT_EVENT_NO_RESPONSE = 1;
static final int REPORT_EVENT_UI_SHOWN = 2;
static final int REPORT_EVENT_UI_DESTROYED = 3;
static final int REPORT_EVENT_INLINE_RESPONSE = 4;
@IntDef(prefix = { "REPORT_EVENT_" }, value = {
REPORT_EVENT_NO_RESPONSE,
REPORT_EVENT_UI_SHOWN,
REPORT_EVENT_UI_DESTROYED
REPORT_EVENT_UI_DESTROYED,
REPORT_EVENT_INLINE_RESPONSE
})
@Retention(RetentionPolicy.SOURCE)
@interface ReportEvent{}
@@ -365,8 +366,8 @@ public abstract class AugmentedAutofillService extends Service {
private final Object mLock = new Object();
private final IAugmentedAutofillManagerClient mClient;
private final int mSessionId;
public final int taskId;
public final ComponentName componentName;
public final int mTaskId;
public final ComponentName mComponentName;
// Used for metrics / debug only
private String mServicePackageName;
@GuardedBy("mLock")
@@ -406,8 +407,8 @@ public abstract class AugmentedAutofillService extends Service {
mSessionId = sessionId;
mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
mCallback = callback;
this.taskId = taskId;
this.componentName = componentName;
mTaskId = taskId;
mComponentName = componentName;
mServicePackageName = serviceComponentName.getPackageName();
mFocusedId = focusedId;
mFocusedValue = focusedValue;
@@ -514,22 +515,24 @@ public abstract class AugmentedAutofillService extends Service {
}
}
public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData,
@Nullable Bundle clientState) {
void reportResult(@Nullable List<Dataset> inlineSuggestionsData) {
try {
mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}), clientState);
final Dataset[] inlineSuggestions = (inlineSuggestionsData != null)
? inlineSuggestionsData.toArray(new Dataset[inlineSuggestionsData.size()])
: null;
mCallback.onSuccess(inlineSuggestions);
} 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);
void logEvent(@ReportEvent int event) {
if (sVerbose) Log.v(TAG, "returnAndLogResult(): " + event);
long duration = -1;
int type = MetricsEvent.TYPE_UNKNOWN;
switch (event) {
case REPORT_EVENT_NO_RESPONSE:
case REPORT_EVENT_NO_RESPONSE: {
type = MetricsEvent.TYPE_SUCCESS;
if (mFirstOnSuccessTime == 0) {
mFirstOnSuccessTime = SystemClock.elapsedRealtime();
@@ -538,40 +541,49 @@ public abstract class AugmentedAutofillService extends Service {
Log.d(TAG, "Service responded nothing in " + formatDuration(duration));
}
}
try {
mCallback.onSuccess(/* inlineSuggestionsData= */null, /* clientState=*/
null);
} catch (RemoteException e) {
Log.e(TAG, "Error reporting success: " + e);
} break;
case REPORT_EVENT_INLINE_RESPONSE: {
// TODO: Define a constant and log this event
// type = MetricsEvent.TYPE_SUCCESS_INLINE;
if (mFirstOnSuccessTime == 0) {
mFirstOnSuccessTime = SystemClock.elapsedRealtime();
duration = mFirstOnSuccessTime - mFirstRequestTime;
if (sDebug) {
Log.d(TAG, "Service responded nothing in " + formatDuration(duration));
}
}
break;
case REPORT_EVENT_UI_SHOWN:
} break;
case REPORT_EVENT_UI_SHOWN: {
type = MetricsEvent.TYPE_OPEN;
if (mUiFirstShownTime == 0) {
mUiFirstShownTime = SystemClock.elapsedRealtime();
duration = mUiFirstShownTime - mFirstRequestTime;
if (sDebug) Log.d(TAG, "UI shown in " + formatDuration(duration));
}
break;
case REPORT_EVENT_UI_DESTROYED:
} break;
case REPORT_EVENT_UI_DESTROYED: {
type = MetricsEvent.TYPE_CLOSE;
if (mUiFirstDestroyedTime == 0) {
mUiFirstDestroyedTime = SystemClock.elapsedRealtime();
duration = mUiFirstDestroyedTime - mFirstRequestTime;
duration = mUiFirstDestroyedTime - mFirstRequestTime;
if (sDebug) Log.d(TAG, "UI destroyed in " + formatDuration(duration));
}
break;
} break;
default:
Log.w(TAG, "invalid event reported: " + event);
}
logResponse(type, mServicePackageName, componentName, mSessionId, duration);
logResponse(type, mServicePackageName, mComponentName, mSessionId, duration);
}
public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("sessionId: "); pw.println(mSessionId);
pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
pw.print(prefix); pw.print("taskId: "); pw.println(mTaskId);
pw.print(prefix); pw.print("component: ");
pw.println(componentName.flattenToShortString());
pw.println(mComponentName.flattenToShortString());
pw.print(prefix); pw.print("focusedId: "); pw.println(mFocusedId);
if (mFocusedValue != null) {
pw.print(prefix); pw.print("focusedValue: "); pw.println(mFocusedValue);

View File

@@ -54,13 +54,15 @@ public final class FillCallback {
if (sDebug) Log.d(TAG, "onSuccess(): " + response);
if (response == null) {
mProxy.report(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
mProxy.logEvent(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
mProxy.reportResult(null /*inlineSuggestions*/);
return;
}
List<Dataset> inlineSuggestions = response.getInlineSuggestions();
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
mProxy.onInlineSuggestionsDataReady(inlineSuggestions, response.getClientState());
mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
mProxy.reportResult(inlineSuggestions);
return;
}

View File

@@ -62,12 +62,13 @@ public final class FillController {
try {
mProxy.autofill(values);
final FillWindow fillWindow = mProxy.getFillWindow();
if (fillWindow != null) {
fillWindow.destroy();
}
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
final FillWindow fillWindow = mProxy.getFillWindow();
if (fillWindow != null) {
fillWindow.destroy();
}
}
}

View File

@@ -53,7 +53,7 @@ public final class FillRequest {
* Gets the task of the activity associated with this request.
*/
public int getTaskId() {
return mProxy.taskId;
return mProxy.mTaskId;
}
/**
@@ -61,7 +61,7 @@ public final class FillRequest {
*/
@NonNull
public ComponentName getActivityComponent() {
return mProxy.componentName;
return mProxy.mComponentName;
}
/**

View File

@@ -21,6 +21,7 @@ import static android.service.autofill.augmented.AugmentedAutofillService.sVerbo
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.graphics.Rect;
@@ -41,6 +42,7 @@ import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
/**
* Handle to a window used to display the augmented autofill UI.
@@ -70,23 +72,22 @@ public final class FillWindow implements AutoCloseable {
private final CloseGuard mCloseGuard = CloseGuard.get();
private final @NonNull Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
private final @NonNull FillWindowPresenter mFillWindowPresenter = new FillWindowPresenter();
@GuardedBy("mLock")
private WindowManager mWm;
private @NonNull WindowManager mWm;
@GuardedBy("mLock")
private View mFillView;
@GuardedBy("mLock")
private boolean mShowing;
@GuardedBy("mLock")
private Rect mBounds;
private @Nullable Rect mBounds;
@GuardedBy("mLock")
private boolean mUpdateCalled;
@GuardedBy("mLock")
private boolean mDestroyed;
private AutofillProxy mProxy;
private @NonNull AutofillProxy mProxy;
/**
* Updates the content of the window.
@@ -172,11 +173,11 @@ public final class FillWindow implements AutoCloseable {
try {
mProxy.requestShowFillUi(mBounds.right - mBounds.left,
mBounds.bottom - mBounds.top,
/*anchorBounds=*/ null, mFillWindowPresenter);
/*anchorBounds=*/ null, new FillWindowPresenter(this));
} catch (RemoteException e) {
Log.w(TAG, "Error requesting to show fill window", e);
}
mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
mProxy.logEvent(AutofillProxy.REPORT_EVENT_UI_SHOWN);
}
}
}
@@ -244,7 +245,7 @@ public final class FillWindow implements AutoCloseable {
if (mUpdateCalled) {
mFillView.setOnClickListener(null);
hide();
mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
mProxy.logEvent(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
}
mDestroyed = true;
mCloseGuard.close();
@@ -254,9 +255,7 @@ public final class FillWindow implements AutoCloseable {
@Override
protected void finalize() throws Throwable {
try {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
mCloseGuard.warnIfOpen();
destroy();
} finally {
super.finalize();
@@ -289,22 +288,36 @@ public final class FillWindow implements AutoCloseable {
/** @hide */
@Override
public void close() throws Exception {
public void close() {
destroy();
}
private final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
private static final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
private final @NonNull WeakReference<FillWindow> mFillWindowReference;
FillWindowPresenter(@NonNull FillWindow fillWindow) {
mFillWindowReference = new WeakReference<>(fillWindow);
}
@Override
public void show(WindowManager.LayoutParams p, Rect transitionEpicenter,
boolean fitsSystemWindows, int layoutDirection) {
if (sDebug) Log.d(TAG, "FillWindowPresenter.show()");
mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleShow, FillWindow.this, p));
final FillWindow fillWindow = mFillWindowReference.get();
if (fillWindow != null) {
fillWindow.mUiThreadHandler.sendMessage(
obtainMessage(FillWindow::handleShow, fillWindow, p));
}
}
@Override
public void hide(Rect transitionEpicenter) {
if (sDebug) Log.d(TAG, "FillWindowPresenter.hide()");
mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleHide, FillWindow.this));
final FillWindow fillWindow = mFillWindowReference.get();
if (fillWindow != null) {
fillWindow.mUiThreadHandler.sendMessage(
obtainMessage(FillWindow::handleHide, fillWindow));
}
}
}
}

View File

@@ -28,7 +28,7 @@ import android.service.autofill.Dataset;
*/
interface IFillCallback {
void onCancellable(in ICancellationSignal cancellation);
void onSuccess(in @nullable Dataset[] inlineSuggestionsData, in @nullable Bundle clientState);
void onSuccess(in @nullable Dataset[] inlineSuggestionsData);
boolean isCompleted();
void cancel();
}