Support WRAP_CONTENT in InlineSuggestion#inflate
Test: atest android.autofillservice.cts.inline Bug: 154887895 Change-Id: Ie420bd40e8f0fb7d6e6b1e47ed0f41dc3f85b25e
This commit is contained in:
@@ -28,7 +28,7 @@ import android.view.SurfaceControlViewHost;
|
||||
oneway interface IInlineSuggestionUiCallback {
|
||||
void onClick();
|
||||
void onLongClick();
|
||||
void onContent(in SurfaceControlViewHost.SurfacePackage surface);
|
||||
void onContent(in SurfaceControlViewHost.SurfacePackage surface, int width, int height);
|
||||
void onError();
|
||||
void onTransferTouchFocusToImeWindow(in IBinder sourceInputToken, int displayId);
|
||||
void onStartIntentSender(in IntentSender intentSender);
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.Service;
|
||||
import android.app.slice.Slice;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.graphics.PixelFormat;
|
||||
@@ -34,14 +33,15 @@ import android.os.Looper;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
import android.view.Display;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/**
|
||||
* A service that renders an inline presentation given the {@link InlinePresentation} containing
|
||||
* a {@link Slice} built using the {@link androidx.autofill.AutofillSliceBuilder}.
|
||||
* A service that renders an inline presentation view given the {@link InlinePresentation}.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
@@ -55,8 +55,8 @@ public abstract class InlineSuggestionRenderService extends Service {
|
||||
* The {@link Intent} that must be declared as handled by the service.
|
||||
*
|
||||
* <p>To be supported, the service must also require the
|
||||
* {@link android.Manifest.permission#BIND_INLINE_SUGGESTION_RENDER_SERVICE} permission so
|
||||
* that other applications can not abuse it.
|
||||
* {@link android.Manifest.permission#BIND_INLINE_SUGGESTION_RENDER_SERVICE} permission so that
|
||||
* other applications can not abuse it.
|
||||
*/
|
||||
public static final String SERVICE_INTERFACE =
|
||||
"android.service.autofill.InlineSuggestionRenderService";
|
||||
@@ -65,6 +65,45 @@ public abstract class InlineSuggestionRenderService extends Service {
|
||||
|
||||
private IInlineSuggestionUiCallback mCallback;
|
||||
|
||||
/**
|
||||
* If the specified {@code width}/{@code height} is an exact value, then it will be returned as
|
||||
* is, otherwise the method tries to measure a size that is just large enough to fit the view
|
||||
* content, within constraints posed by {@code minSize} and {@code maxSize}.
|
||||
*
|
||||
* @param view the view for which we measure the size
|
||||
* @param width the expected width of the view, either an exact value or {@link
|
||||
* ViewGroup.LayoutParams#WRAP_CONTENT}
|
||||
* @param height the expected width of the view, either an exact value or {@link
|
||||
* ViewGroup.LayoutParams#WRAP_CONTENT}
|
||||
* @param minSize the lower bound of the size to be returned
|
||||
* @param maxSize the upper bound of the size to be returned
|
||||
* @return the measured size of the view based on the given size constraints.
|
||||
*/
|
||||
private Size measuredSize(@NonNull View view, int width, int height, @NonNull Size minSize,
|
||||
@NonNull Size maxSize) {
|
||||
if (width != ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
&& height != ViewGroup.LayoutParams.WRAP_CONTENT) {
|
||||
return new Size(width, height);
|
||||
}
|
||||
int widthMeasureSpec;
|
||||
if (width == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
||||
widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxSize.getWidth(),
|
||||
View.MeasureSpec.AT_MOST);
|
||||
} else {
|
||||
widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
|
||||
}
|
||||
int heightMeasureSpec;
|
||||
if (height == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
||||
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxSize.getHeight(),
|
||||
View.MeasureSpec.AT_MOST);
|
||||
} else {
|
||||
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
|
||||
}
|
||||
view.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
return new Size(Math.max(view.getMeasuredWidth(), minSize.getWidth()),
|
||||
Math.max(view.getMeasuredHeight(), minSize.getHeight()));
|
||||
}
|
||||
|
||||
private void handleRenderSuggestion(IInlineSuggestionUiCallback callback,
|
||||
InlinePresentation presentation, int width, int height, IBinder hostInputToken,
|
||||
int displayId) {
|
||||
@@ -82,6 +121,7 @@ public abstract class InlineSuggestionRenderService extends Service {
|
||||
try {
|
||||
final View suggestionView = onRenderSuggestion(presentation, width, height);
|
||||
if (suggestionView == null) {
|
||||
Log.w(TAG, "ExtServices failed to render the inline suggestion view.");
|
||||
try {
|
||||
callback.onError();
|
||||
} catch (RemoteException e) {
|
||||
@@ -90,13 +130,16 @@ public abstract class InlineSuggestionRenderService extends Service {
|
||||
return;
|
||||
}
|
||||
mCallback = callback;
|
||||
final Size measuredSize = measuredSize(suggestionView, width, height,
|
||||
presentation.getInlinePresentationSpec().getMinSize(),
|
||||
presentation.getInlinePresentationSpec().getMaxSize());
|
||||
Log.v(TAG, "width=" + width + ", height=" + height + ", measuredSize=" + measuredSize);
|
||||
|
||||
final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback);
|
||||
suggestionRoot.addView(suggestionView);
|
||||
WindowManager.LayoutParams lp =
|
||||
new WindowManager.LayoutParams(width, height,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION, 0,
|
||||
PixelFormat.TRANSPARENT);
|
||||
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(measuredSize.getWidth(),
|
||||
measuredSize.getHeight(), WindowManager.LayoutParams.TYPE_APPLICATION, 0,
|
||||
PixelFormat.TRANSPARENT);
|
||||
|
||||
final SurfaceControlViewHost host = new SurfaceControlViewHost(this, getDisplay(),
|
||||
hostInputToken);
|
||||
@@ -124,7 +167,8 @@ public abstract class InlineSuggestionRenderService extends Service {
|
||||
return true;
|
||||
});
|
||||
|
||||
sendResult(callback, host.getSurfacePackage());
|
||||
sendResult(callback, host.getSurfacePackage(), measuredSize.getWidth(),
|
||||
measuredSize.getHeight());
|
||||
} finally {
|
||||
updateDisplay(Display.DEFAULT_DISPLAY);
|
||||
}
|
||||
@@ -136,9 +180,9 @@ public abstract class InlineSuggestionRenderService extends Service {
|
||||
}
|
||||
|
||||
private void sendResult(@NonNull IInlineSuggestionUiCallback callback,
|
||||
@Nullable SurfaceControlViewHost.SurfacePackage surface) {
|
||||
@Nullable SurfaceControlViewHost.SurfacePackage surface, int width, int height) {
|
||||
try {
|
||||
callback.onContent(surface);
|
||||
callback.onContent(surface, width, height);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "RemoteException calling onContent(" + surface + ")");
|
||||
}
|
||||
@@ -154,10 +198,10 @@ public abstract class InlineSuggestionRenderService extends Service {
|
||||
public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
|
||||
@NonNull InlinePresentation presentation, int width, int height,
|
||||
@Nullable IBinder hostInputToken, int displayId) {
|
||||
mHandler.sendMessage(obtainMessage(
|
||||
InlineSuggestionRenderService::handleRenderSuggestion,
|
||||
InlineSuggestionRenderService.this, callback, presentation,
|
||||
width, height, hostInputToken, displayId));
|
||||
mHandler.sendMessage(
|
||||
obtainMessage(InlineSuggestionRenderService::handleRenderSuggestion,
|
||||
InlineSuggestionRenderService.this, callback, presentation,
|
||||
width, height, hostInputToken, displayId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,7 +220,8 @@ public abstract class InlineSuggestionRenderService extends Service {
|
||||
/**
|
||||
* Starts the {@link IntentSender} from the client app.
|
||||
*
|
||||
* @param intentSender the {@link IntentSender} to start the attribution UI from the client app.
|
||||
* @param intentSender the {@link IntentSender} to start the attribution UI from the client
|
||||
* app.
|
||||
*/
|
||||
public final void startIntentSender(@NonNull IntentSender intentSender) {
|
||||
if (mCallback == null) return;
|
||||
@@ -188,8 +233,8 @@ public abstract class InlineSuggestionRenderService extends Service {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the metadata about the renderer. Returns {@code Bundle.Empty} if no metadata is
|
||||
* provided.
|
||||
* Returns the metadata about the renderer. Returns {@code Bundle.Empty} if no metadata is
|
||||
* provided.
|
||||
*/
|
||||
@NonNull
|
||||
public Bundle onGetInlineSuggestionsRendererInfo() {
|
||||
@@ -200,8 +245,8 @@ public abstract class InlineSuggestionRenderService extends Service {
|
||||
* Renders the slice into a view.
|
||||
*/
|
||||
@Nullable
|
||||
public View onRenderSuggestion(@NonNull InlinePresentation presentation,
|
||||
int width, int height) {
|
||||
public View onRenderSuggestion(@NonNull InlinePresentation presentation, int width,
|
||||
int height) {
|
||||
Log.e(TAG, "service implementation (" + getClass() + " does not implement "
|
||||
+ "onRenderSuggestion()");
|
||||
return null;
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.os.RemoteException;
|
||||
import android.util.Size;
|
||||
import android.util.Slog;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.inline.InlineContentView;
|
||||
|
||||
import com.android.internal.util.DataClass;
|
||||
@@ -94,19 +95,26 @@ public final class InlineSuggestion implements Parcelable {
|
||||
|
||||
/**
|
||||
* Inflates a view with the content of this suggestion at a specific size.
|
||||
* The size must be between the
|
||||
*
|
||||
* <p> The size must be either 1) between the
|
||||
* {@link android.widget.inline.InlinePresentationSpec#getMinSize() min size} and the
|
||||
* {@link android.widget.inline.InlinePresentationSpec#getMaxSize() max size} of the
|
||||
* presentation spec returned by {@link InlineSuggestionInfo#getInlinePresentationSpec()}.
|
||||
* presentation spec returned by {@link InlineSuggestionInfo#getInlinePresentationSpec()},
|
||||
* or 2) {@link ViewGroup.LayoutParams#WRAP_CONTENT}. If the size is set to
|
||||
* {@link ViewGroup.LayoutParams#WRAP_CONTENT}, then the size of the inflated view will be just
|
||||
* large enough to fit the content, while still conforming to the min / max size specified by
|
||||
* the {@link android.widget.inline.InlinePresentationSpec}.
|
||||
*
|
||||
* <p> The caller can attach an {@link android.view.View.OnClickListener} and/or an
|
||||
* {@link android.view.View.OnLongClickListener} to the view in the
|
||||
* {@code callback} to receive click and
|
||||
* long click events on the view.
|
||||
* {@code callback} to receive click and long click events on the view.
|
||||
*
|
||||
* @param context Context in which to inflate the view.
|
||||
* @param size The size at which to inflate the suggestion.
|
||||
* @param callback Callback for receiving the inflated view.
|
||||
* @param size The size at which to inflate the suggestion. For each dimension, it maybe
|
||||
* an exact value or {@link ViewGroup.LayoutParams#WRAP_CONTENT}.
|
||||
* @param callback Callback for receiving the inflated view, where the
|
||||
* {@link ViewGroup.LayoutParams} of the view is set as the actual size of
|
||||
* the underlying remote view.
|
||||
* @throws IllegalArgumentException If an invalid argument is passed.
|
||||
* @throws IllegalStateException If this method is already called.
|
||||
*/
|
||||
@@ -115,10 +123,11 @@ public final class InlineSuggestion implements Parcelable {
|
||||
@NonNull Consumer<InlineContentView> callback) {
|
||||
final Size minSize = mInfo.getInlinePresentationSpec().getMinSize();
|
||||
final Size maxSize = mInfo.getInlinePresentationSpec().getMaxSize();
|
||||
if (size.getHeight() < minSize.getHeight() || size.getHeight() > maxSize.getHeight()
|
||||
|| size.getWidth() < minSize.getWidth() || size.getWidth() > maxSize.getWidth()) {
|
||||
throw new IllegalArgumentException("size not between min:"
|
||||
+ minSize + " and max:" + maxSize);
|
||||
if (!isValid(size.getWidth(), minSize.getWidth(), maxSize.getWidth())
|
||||
|| !isValid(size.getHeight(), minSize.getHeight(), maxSize.getHeight())) {
|
||||
throw new IllegalArgumentException(
|
||||
"size is neither between min:" + minSize + " and max:" + maxSize
|
||||
+ ", nor wrap_content");
|
||||
}
|
||||
mInlineContentCallback = getInlineContentCallback(context, callbackExecutor, callback);
|
||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||
@@ -136,6 +145,17 @@ public final class InlineSuggestion implements Parcelable {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the {@code actual} length is within [min, max] or is {@link
|
||||
* ViewGroup.LayoutParams#WRAP_CONTENT}.
|
||||
*/
|
||||
private static boolean isValid(int actual, int min, int max) {
|
||||
if (actual == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
||||
return true;
|
||||
}
|
||||
return actual >= min && actual <= max;
|
||||
}
|
||||
|
||||
private synchronized InlineContentCallbackImpl getInlineContentCallback(Context context,
|
||||
Executor callbackExecutor, Consumer<InlineContentView> callback) {
|
||||
if (mInlineContentCallback != null) {
|
||||
@@ -154,10 +174,11 @@ public final class InlineSuggestion implements Parcelable {
|
||||
|
||||
@Override
|
||||
@BinderThread
|
||||
public void onContent(SurfaceControlViewHost.SurfacePackage content) {
|
||||
public void onContent(SurfaceControlViewHost.SurfacePackage content, int width,
|
||||
int height) {
|
||||
final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get();
|
||||
if (callbackImpl != null) {
|
||||
callbackImpl.onContent(content);
|
||||
callbackImpl.onContent(content, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,11 +217,13 @@ public final class InlineSuggestion implements Parcelable {
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
public void onContent(SurfaceControlViewHost.SurfacePackage content) {
|
||||
public void onContent(SurfaceControlViewHost.SurfacePackage content, int width,
|
||||
int height) {
|
||||
if (content == null) {
|
||||
mCallbackExecutor.execute(() -> mCallback.accept(/* view */null));
|
||||
} else {
|
||||
mView = new InlineContentView(mContext);
|
||||
mView.setLayoutParams(new ViewGroup.LayoutParams(width, height));
|
||||
mView.setChildSurfacePackage(content);
|
||||
mCallbackExecutor.execute(() -> mCallback.accept(mView));
|
||||
}
|
||||
@@ -398,10 +421,10 @@ public final class InlineSuggestion implements Parcelable {
|
||||
};
|
||||
|
||||
@DataClass.Generated(
|
||||
time = 1585180783541L,
|
||||
time = 1587771173367L,
|
||||
codegenVersion = "1.0.15",
|
||||
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
|
||||
inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
|
||||
inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate static boolean isValid(int,int,int)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.view.SurfaceControlViewHost;
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IInlineContentCallback {
|
||||
void onContent(in SurfaceControlViewHost.SurfacePackage content);
|
||||
void onContent(in SurfaceControlViewHost.SurfacePackage content, int width, int height);
|
||||
void onClick();
|
||||
void onLongClick();
|
||||
}
|
||||
|
||||
@@ -319,9 +319,10 @@ public final class InlineSuggestionFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContent(SurfaceControlViewHost.SurfacePackage surface)
|
||||
public void onContent(SurfaceControlViewHost.SurfacePackage surface, int width,
|
||||
int height)
|
||||
throws RemoteException {
|
||||
callback.onContent(surface);
|
||||
callback.onContent(surface, width, height);
|
||||
surface.release();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user