Merge "Add APIs to move suggestions below/above window" into rvc-dev
diff --git a/api/current.txt b/api/current.txt
index eb8ee3c..c19a70c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -56772,6 +56772,19 @@
 
 package android.view.inline {
 
+  public class InlineContentView extends android.view.ViewGroup {
+    method @Nullable public android.view.SurfaceControl getSurfaceControl();
+    method public boolean isZOrderedOnTop();
+    method public void onLayout(boolean, int, int, int, int);
+    method public void setSurfaceControlCallback(@Nullable android.view.inline.InlineContentView.SurfaceControlCallback);
+    method public boolean setZOrderedOnTop(boolean);
+  }
+
+  public static interface InlineContentView.SurfaceControlCallback {
+    method public void onCreated(@NonNull android.view.SurfaceControl);
+    method public void onDestroyed(@NonNull android.view.SurfaceControl);
+  }
+
   public final class InlinePresentationSpec implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.util.Size getMaxSize();
@@ -56960,7 +56973,7 @@
   public final class InlineSuggestion implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.view.inputmethod.InlineSuggestionInfo getInfo();
-    method public void inflate(@NonNull android.content.Context, @NonNull android.util.Size, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.View>);
+    method public void inflate(@NonNull android.content.Context, @NonNull android.util.Size, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.inline.InlineContentView>);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InlineSuggestion> CREATOR;
   }
diff --git a/core/java/android/service/autofill/InlineSuggestionRoot.java b/core/java/android/service/autofill/InlineSuggestionRoot.java
index 6c9d36b..653e513 100644
--- a/core/java/android/service/autofill/InlineSuggestionRoot.java
+++ b/core/java/android/service/autofill/InlineSuggestionRoot.java
@@ -68,7 +68,9 @@
             case MotionEvent.ACTION_MOVE: {
                 final float distance = MathUtils.dist(mDownX, mDownY,
                         event.getX(), event.getY());
-                if (distance > mTouchSlop) {
+                final boolean isSecure = (event.getFlags()
+                        & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) == 0;
+                if (!isSecure || distance > mTouchSlop) {
                     try {
                         mCallback.onTransferTouchFocusToImeWindow(getViewRootImpl().getInputToken(),
                                 getContext().getDisplayId());
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 3e1e393..59fc6e9 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -686,16 +686,107 @@
      * SurfaceView is in will be visible on top of its surface.
      *
      * <p>Note that this must be set before the surface view's containing
-     * window is attached to the window manager.
+     * window is attached to the window manager. If you target {@link Build.VERSION_CODES#R}
+     * the Z ordering can be changed dynamically if the backing surface is
+     * created, otherwise it would be applied at surface construction time.
      *
      * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
+     *
+     * @param onTop Whether to show the surface on top of this view's window.
      */
     public void setZOrderOnTop(boolean onTop) {
+        // In R and above we allow dynamic layer changes.
+        final boolean allowDynamicChange = getContext().getApplicationInfo().targetSdkVersion
+                > Build.VERSION_CODES.Q;
+        setZOrderedOnTop(onTop, allowDynamicChange);
+    }
+
+    /**
+     * @return Whether the surface backing this view appears on top of its parent.
+     *
+     * @hide
+     */
+    public boolean isZOrderedOnTop() {
+        return mSubLayer > 0;
+    }
+
+    /**
+     * Controls whether the surface view's surface is placed on top of its
+     * window. Normally it is placed behind the window, to allow it to
+     * (for the most part) appear to composite with the views in the
+     * hierarchy. By setting this, you cause it to be placed above the
+     * window. This means that none of the contents of the window this
+     * SurfaceView is in will be visible on top of its surface.
+     *
+     * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
+     *
+     * @param onTop Whether to show the surface on top of this view's window.
+     * @param allowDynamicChange Whether this can happen after the surface is created.
+     * @return Whether the Z ordering changed.
+     *
+     * @hide
+     */
+    public boolean setZOrderedOnTop(boolean onTop, boolean allowDynamicChange) {
+        final int subLayer;
         if (onTop) {
-            mSubLayer = APPLICATION_PANEL_SUBLAYER;
+            subLayer = APPLICATION_PANEL_SUBLAYER;
         } else {
-            mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+            subLayer = APPLICATION_MEDIA_SUBLAYER;
         }
+        if (mSubLayer == subLayer) {
+            return false;
+        }
+        mSubLayer = subLayer;
+
+        if (!allowDynamicChange) {
+            return false;
+        }
+        if (mSurfaceControl == null) {
+            return true;
+        }
+        final ViewRootImpl viewRoot = getViewRootImpl();
+        if (viewRoot == null) {
+            return true;
+        }
+        final Surface parent = viewRoot.mSurface;
+        if (parent == null || !parent.isValid()) {
+            return true;
+        }
+
+        /*
+         * Schedule a callback that reflects an alpha value onto the underlying surfaces.
+         * This gets called on a RenderThread worker thread, so members accessed here must
+         * be protected by a lock.
+         */
+        final boolean useBLAST = viewRoot.useBLAST();
+        viewRoot.registerRtFrameCallback(frame -> {
+            try {
+                final SurfaceControl.Transaction t = useBLAST
+                        ? viewRoot.getBLASTSyncTransaction()
+                        : new SurfaceControl.Transaction();
+                synchronized (mSurfaceControlLock) {
+                    if (!parent.isValid() || mSurfaceControl == null) {
+                        return;
+                    }
+                    updateRelativeZ(t);
+                    if (!useBLAST) {
+                        t.deferTransactionUntil(mSurfaceControl,
+                                viewRoot.getRenderSurfaceControl(), frame);
+                    }
+                }
+                // It's possible that mSurfaceControl is released in the UI thread before
+                // the transaction completes. If that happens, an exception is thrown, which
+                // must be caught immediately.
+                t.apply();
+             } catch (Exception e) {
+                Log.e(TAG, System.identityHashCode(this)
+                        + "setZOrderOnTop RT: Exception during surface transaction", e);
+            }
+        });
+
+        invalidate();
+
+        return true;
     }
 
     /**
diff --git a/core/java/android/view/inline/InlineContentView.java b/core/java/android/view/inline/InlineContentView.java
index 2a8ca0b..df5bc2f 100644
--- a/core/java/android/view/inline/InlineContentView.java
+++ b/core/java/android/view/inline/InlineContentView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,22 +17,189 @@
 package android.view.inline;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.PixelFormat;
+import android.util.AttributeSet;
+import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
+import android.view.SurfaceHolder;
 import android.view.SurfaceView;
+import android.view.ViewGroup;
 
 /**
- * This class represents a view that can hold an opaque content that may be from a different source.
+ * This class represents a view that holds opaque content from another app that
+ * you can inline in your UI.
  *
- * @hide
+ * <p>Since the content presented by this view is from another security domain,it is
+ * shown on a remote surface preventing the host application from accessing that content.
+ * Also the host application cannot interact with the inlined content by injecting touch
+ * events or clicking programmatically.
+ *
+ * <p>This view can be overlaid by other windows, i.e. redressed, but if this is the case
+ * the inined UI would not be interactive. Sometimes this is desirable, e.g. animating
+ * transitions.
+ *
+ * <p>By default the surface backing this view is shown on top of the hosting window such
+ * that the inlined content is interactive. However, you can temporarily move the surface
+ * under the hosting window which could be useful in some cases, e.g. animating transitions.
+ * At this point the inlined content will not be interactive and the touch events would
+ * be delivered to your app.
  */
-public class InlineContentView extends SurfaceView {
-    public InlineContentView(@NonNull Context context,
-            @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
-        super(context);
-        setZOrderOnTop(true);
-        setChildSurfacePackage(surfacePackage);
-        getHolder().setFormat(PixelFormat.TRANSPARENT);
+public class InlineContentView extends ViewGroup {
+
+    /**
+     * Callback for observing the lifecycle of the surface control
+     * that manipulates the backing secure embedded UI surface.
+     */
+    public interface SurfaceControlCallback {
+        /**
+         * Called when the backing surface is being created.
+         *
+         * @param surfaceControl The surface control to manipulate the surface.
+         */
+        void onCreated(@NonNull SurfaceControl surfaceControl);
+
+        /**
+         * Called when the backing surface is being destroyed.
+         *
+         * @param surfaceControl The surface control to manipulate the surface.
+         */
+        void onDestroyed(@NonNull SurfaceControl surfaceControl);
+    }
+
+    private final @NonNull SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
+        @Override
+        public void surfaceCreated(@NonNull SurfaceHolder holder) {
+            mSurfaceControlCallback.onCreated(mSurfaceView.getSurfaceControl());
+        }
+
+        @Override
+        public void surfaceChanged(@NonNull SurfaceHolder holder,
+                int format, int width, int height) {
+            /* do nothing */
+        }
+
+        @Override
+        public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+            mSurfaceControlCallback.onDestroyed(mSurfaceView.getSurfaceControl());
+        }
+    };
+
+    private final @NonNull SurfaceView mSurfaceView;
+
+    private @Nullable SurfaceControlCallback mSurfaceControlCallback;
+
+    /**
+     * @inheritDoc
+     *
+     * @hide
+     */
+    public InlineContentView(@NonNull Context context) {
+        this(context, null);
+    }
+
+    /**
+     * @inheritDoc
+     *
+     * @hide
+     */
+    public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    /**
+     * @inheritDoc
+     *
+     * @hide
+     */
+    public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    /**
+     * Gets the surface control. If the surface is not created this method
+     * returns {@code null}.
+     *
+     * @return The surface control.
+     *
+     * @see #setSurfaceControlCallback(SurfaceControlCallback) 
+     */
+    public @Nullable SurfaceControl getSurfaceControl() {
+        return mSurfaceView.getSurfaceControl();
+    }
+
+    /**
+     * @inheritDoc
+     *
+     * @hide
+     */
+    public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mSurfaceView = new SurfaceView(context, attrs, defStyleAttr, defStyleRes);
+        mSurfaceView.setZOrderOnTop(true);
+        mSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
+        addView(mSurfaceView);
+    }
+
+    /**
+     * Sets the embedded UI.
+     * @param surfacePackage The embedded UI.
+     *
+     * @hide
+     */
+    public void setChildSurfacePackage(
+            @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) {
+        mSurfaceView.setChildSurfacePackage(surfacePackage);
+    }
+
+    @Override
+    public void onLayout(boolean changed, int l, int t, int r, int b) {
+        mSurfaceView.layout(l, t, r, b);
+    }
+
+    /**
+     * Sets a callback to observe the lifecycle of the surface control for
+     * managing the backing surface.
+     *
+     * @param callback The callback to set or {@code null} to clear.
+     */
+    public void setSurfaceControlCallback(@Nullable SurfaceControlCallback callback) {
+        if (mSurfaceControlCallback != null) {
+            mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
+        }
+        mSurfaceControlCallback = callback;
+        if (mSurfaceControlCallback != null) {
+            mSurfaceView.getHolder().addCallback(mSurfaceCallback);
+        }
+    }
+
+    /**
+     * @return Whether the surface backing this view appears on top of its parent.
+     *
+     * @see #setZOrderedOnTop(boolean)
+     */
+    public boolean isZOrderedOnTop() {
+        return mSurfaceView.isZOrderedOnTop();
+    }
+
+    /**
+     * Controls whether the backing surface is placed on top of this view's window.
+     * Normally, it is placed on top of the window, to allow interaction
+     * with the inlined UI. Via this method, you can place the surface below the
+     * window. This means that all of the contents of the window this view is in
+     * will be visible on top of its surface.
+     *
+     * <p> The Z ordering can be changed dynamically if the backing surface is
+     * created, otherwise the ordering would be applied at surface construction time.
+     *
+     * @param onTop Whether to show the surface on top of this view's window.
+     *
+     * @see #isZOrderedOnTop()
+     */
+    public boolean setZOrderedOnTop(boolean onTop) {
+        return mSurfaceView.setZOrderedOnTop(onTop, /*allowDynamicChange*/ true);
     }
 }
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index dd1738a..ab8f36d 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -29,7 +29,6 @@
 import android.util.Size;
 import android.util.Slog;
 import android.view.SurfaceControlViewHost;
-import android.view.View;
 import android.view.inline.InlineContentView;
 import android.view.inline.InlinePresentationSpec;
 
@@ -94,15 +93,15 @@
         this(info, contentProvider, /* inlineContentCallback */ null);
     }
 
-
     /**
      * Inflates a view with the content of this suggestion at a specific size.
      * The size must be between the {@link InlinePresentationSpec#getMinSize() min size}
      * and the {@link InlinePresentationSpec#getMaxSize() max size} of the presentation
      * spec returned by {@link InlineSuggestionInfo#getPresentationSpec()}.
      *
-     * <p> The caller can attach an {@link View.OnClickListener} and/or an
-     * {@link View.OnLongClickListener} to the view in the {@code callback} to receive click and
+     * <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.
      *
      * @param context  Context in which to inflate the view.
@@ -113,7 +112,7 @@
      */
     public void inflate(@NonNull Context context, @NonNull Size size,
             @NonNull @CallbackExecutor Executor callbackExecutor,
-            @NonNull Consumer<View> callback) {
+            @NonNull Consumer<InlineContentView> callback) {
         final Size minSize = mInfo.getPresentationSpec().getMinSize();
         final Size maxSize = mInfo.getPresentationSpec().getMaxSize();
         if (size.getHeight() < minSize.getHeight() || size.getHeight() > maxSize.getHeight()
@@ -138,7 +137,7 @@
     }
 
     private synchronized InlineContentCallbackImpl getInlineContentCallback(Context context,
-            Executor callbackExecutor, Consumer<View> callback) {
+            Executor callbackExecutor, Consumer<InlineContentView> callback) {
         if (mInlineContentCallback != null) {
             throw new IllegalStateException("Already called #inflate()");
         }
@@ -185,12 +184,12 @@
 
         private final @NonNull Context mContext;
         private final @NonNull Executor mCallbackExecutor;
-        private final @NonNull Consumer<View> mCallback;
-        private @Nullable View mView;
+        private final @NonNull Consumer<InlineContentView> mCallback;
+        private @Nullable InlineContentView mView;
 
         InlineContentCallbackImpl(@NonNull Context context,
                 @NonNull @CallbackExecutor Executor callbackExecutor,
-                @NonNull Consumer<View> callback) {
+                @NonNull Consumer<InlineContentView> callback) {
             mContext = context;
             mCallbackExecutor = callbackExecutor;
             mCallback = callback;
@@ -201,7 +200,8 @@
             if (content == null) {
                 mCallbackExecutor.execute(() -> mCallback.accept(/* view */null));
             } else {
-                mView = new InlineContentView(mContext, content);
+                mView = new InlineContentView(mContext);
+                mView.setChildSurfacePackage(content);
                 mCallbackExecutor.execute(() -> mCallback.accept(mView));
             }
         }
@@ -398,10 +398,10 @@
     };
 
     @DataClass.Generated(
-            time = 1583889058241L,
+            time = 1584679775946L,
             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.view.View>)\nprivate synchronized  android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\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.view.inline.InlineContentView>)\nprivate synchronized  android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.view.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() {}