diff options
5 files changed, 109 insertions, 40 deletions
diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl index bed4302bcd4d..172cfef9fee2 100644 --- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl +++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl @@ -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); diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java index d3ad1c6b8862..19961e5efc6a 100644 --- a/core/java/android/service/autofill/InlineSuggestionRenderService.java +++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java @@ -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; diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java index f50f0dea0466..6b1a480986c8 100644 --- a/core/java/android/view/inputmethod/InlineSuggestion.java +++ b/core/java/android/view/inputmethod/InlineSuggestion.java @@ -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() {} diff --git a/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl b/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl index feb3f026b806..64f7fa77e485 100644 --- a/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl +++ b/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl @@ -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(); } diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java index a86d34d4eb3e..79c9efa48d73 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -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(); } |