diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl index bed4302bcd4db..172cfef9fee26 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 d3ad1c6b88621..19961e5efc6a0 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. * *
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 f50f0dea0466e..6b1a480986c8e 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 + * + *
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}. * *
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