/* * 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget.inline; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.content.Context; import android.graphics.PixelFormat; import android.graphics.PointF; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import java.lang.ref.WeakReference; import java.util.function.Consumer; /** * This class represents a view that holds opaque content from another app that you can inline in * your UI. * *
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. * *
This view can be overlaid by other windows, i.e. redressed, but if this is the case * the inlined UI would not be interactive. Sometimes this is desirable, e.g. animating transitions. * *
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. * *
Instances of this class are created by the platform and can be programmatically attached to
* your UI. Once the view is attached to the window, you may detach and reattach it to the window.
* It should work seamlessly from the hosting process's point of view.
*/
public class InlineContentView extends ViewGroup {
private static final String TAG = "InlineContentView";
private static final boolean DEBUG = false;
/**
* 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);
}
/**
* Callback for sending an updated surface package in case the previous one is released
* from the detached from window event, and for getting notified of such event.
*
* This is expected to be provided to the {@link InlineContentView} so it can get updates
* from and send updates to the remote content (i.e. surface package) provider.
*
* @hide
*/
@TestApi
public interface SurfacePackageUpdater {
/**
* Called when the previous surface package is released due to view being detached
* from the window.
*/
void onSurfacePackageReleased();
/**
* Called to request an updated surface package.
*
* @param consumer consumes the updated surface package.
*/
void getSurfacePackage(@NonNull Consumer 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);
}
private void computeParentPositionAndScale() {
boolean contentPositionOrScaleChanged = false;
// This method can be called on the UI or render thread but for the cases
// it is called these threads are not running concurrently, so no need to lock.
final SurfaceView parentSurfaceOwnerView = (mParentSurfaceOwnerView != null)
? mParentSurfaceOwnerView.get() : null;
if (parentSurfaceOwnerView != null) {
if (mParentPosition == null) {
mParentPosition = new int[2];
}
final int oldParentPositionX = mParentPosition[0];
final int oldParentPositionY = mParentPosition[1];
parentSurfaceOwnerView.getLocationInSurface(mParentPosition);
if (oldParentPositionX != mParentPosition[0]
|| oldParentPositionY != mParentPosition[1]) {
contentPositionOrScaleChanged = true;
}
if (mParentScale == null) {
mParentScale = new PointF();
}
final float lastParentSurfaceWidth = parentSurfaceOwnerView
.getSurfaceRenderPosition().width();
final float oldParentScaleX = mParentScale.x;
if (lastParentSurfaceWidth > 0) {
mParentScale.x = lastParentSurfaceWidth /
(float) parentSurfaceOwnerView.getWidth();
} else {
mParentScale.x = 1.0f;
}
if (!contentPositionOrScaleChanged
&& Float.compare(oldParentScaleX, mParentScale.x) != 0) {
contentPositionOrScaleChanged = true;
}
final float lastParentSurfaceHeight = parentSurfaceOwnerView
.getSurfaceRenderPosition().height();
final float oldParentScaleY = mParentScale.y;
if (lastParentSurfaceHeight > 0) {
mParentScale.y = lastParentSurfaceHeight
/ (float) parentSurfaceOwnerView.getHeight();
} else {
mParentScale.y = 1.0f;
}
if (!contentPositionOrScaleChanged
&& Float.compare(oldParentScaleY, mParentScale.y) != 0) {
contentPositionOrScaleChanged = true;
}
} else if (mParentPosition != null || mParentScale != null) {
contentPositionOrScaleChanged = true;
mParentPosition = null;
mParentScale = null;
}
if (contentPositionOrScaleChanged) {
mSurfaceView.requestUpdateSurfacePositionAndScale();
}
}
}