blob: 9712311aab7c40c5292b64341157c337f8697200 [file] [log] [blame]
Adam Hef27433a2020-03-25 15:11:18 -07001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget.inline;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
Svet Ganova5b49902020-06-18 00:47:22 -070021import android.annotation.TestApi;
Adam Hef27433a2020-03-25 15:11:18 -070022import android.content.Context;
23import android.graphics.PixelFormat;
Svet Ganova5b49902020-06-18 00:47:22 -070024import android.graphics.PointF;
Qi (wangqi) Wangdb0dbea2020-06-24 19:42:20 -070025import android.graphics.Rect;
Adam Hef27433a2020-03-25 15:11:18 -070026import android.util.AttributeSet;
Feng Caob46851c2020-04-29 18:22:49 -070027import android.util.Log;
Adam Hef27433a2020-03-25 15:11:18 -070028import android.view.SurfaceControl;
29import android.view.SurfaceControlViewHost;
30import android.view.SurfaceHolder;
31import android.view.SurfaceView;
Svet Ganova5b49902020-06-18 00:47:22 -070032import android.view.View;
Adam Hef27433a2020-03-25 15:11:18 -070033import android.view.ViewGroup;
Svet Ganova5b49902020-06-18 00:47:22 -070034import android.view.ViewTreeObserver;
Adam Hef27433a2020-03-25 15:11:18 -070035
Svet Ganova5b49902020-06-18 00:47:22 -070036import java.lang.ref.WeakReference;
Feng Caob46851c2020-04-29 18:22:49 -070037import java.util.function.Consumer;
38
Adam Hef27433a2020-03-25 15:11:18 -070039/**
Feng Caob46851c2020-04-29 18:22:49 -070040 * This class represents a view that holds opaque content from another app that you can inline in
41 * your UI.
Adam Hef27433a2020-03-25 15:11:18 -070042 *
43 * <p>Since the content presented by this view is from another security domain,it is
Feng Caob46851c2020-04-29 18:22:49 -070044 * shown on a remote surface preventing the host application from accessing that content. Also the
45 * host application cannot interact with the inlined content by injecting touch events or clicking
46 * programmatically.
Adam Hef27433a2020-03-25 15:11:18 -070047 *
48 * <p>This view can be overlaid by other windows, i.e. redressed, but if this is the case
Feng Caob46851c2020-04-29 18:22:49 -070049 * the inlined UI would not be interactive. Sometimes this is desirable, e.g. animating transitions.
Adam Hef27433a2020-03-25 15:11:18 -070050 *
51 * <p>By default the surface backing this view is shown on top of the hosting window such
Feng Caob46851c2020-04-29 18:22:49 -070052 * that the inlined content is interactive. However, you can temporarily move the surface under the
53 * hosting window which could be useful in some cases, e.g. animating transitions. At this point the
54 * inlined content will not be interactive and the touch events would be delivered to your app.
55 *
56 * <p> Instances of this class are created by the platform and can be programmatically attached to
57 * your UI. Once the view is attached to the window, you may detach and reattach it to the window.
58 * It should work seamlessly from the hosting process's point of view.
Adam Hef27433a2020-03-25 15:11:18 -070059 */
60public class InlineContentView extends ViewGroup {
61
Feng Caob46851c2020-04-29 18:22:49 -070062 private static final String TAG = "InlineContentView";
63
64 private static final boolean DEBUG = false;
65
Adam Hef27433a2020-03-25 15:11:18 -070066 /**
Feng Caob46851c2020-04-29 18:22:49 -070067 * Callback for observing the lifecycle of the surface control that manipulates the backing
68 * secure embedded UI surface.
Adam Hef27433a2020-03-25 15:11:18 -070069 */
70 public interface SurfaceControlCallback {
71 /**
72 * Called when the backing surface is being created.
73 *
74 * @param surfaceControl The surface control to manipulate the surface.
75 */
76 void onCreated(@NonNull SurfaceControl surfaceControl);
77
78 /**
79 * Called when the backing surface is being destroyed.
80 *
81 * @param surfaceControl The surface control to manipulate the surface.
82 */
83 void onDestroyed(@NonNull SurfaceControl surfaceControl);
84 }
85
Feng Caob46851c2020-04-29 18:22:49 -070086 /**
87 * Callback for sending an updated surface package in case the previous one is released
88 * from the detached from window event, and for getting notified of such event.
89 *
90 * This is expected to be provided to the {@link InlineContentView} so it can get updates
91 * from and send updates to the remote content (i.e. surface package) provider.
92 *
93 * @hide
94 */
Svet Ganova5b49902020-06-18 00:47:22 -070095 @TestApi
Feng Caob46851c2020-04-29 18:22:49 -070096 public interface SurfacePackageUpdater {
97
Svet Ganova5b49902020-06-18 00:47:22 -070098
Feng Caob46851c2020-04-29 18:22:49 -070099 /**
100 * Called when the previous surface package is released due to view being detached
101 * from the window.
102 */
103 void onSurfacePackageReleased();
104
105 /**
106 * Called to request an updated surface package.
107 *
108 * @param consumer consumes the updated surface package.
109 */
Svet Ganova5b49902020-06-18 00:47:22 -0700110 void getSurfacePackage(@NonNull Consumer<SurfaceControlViewHost.SurfacePackage> consumer);
Feng Caob46851c2020-04-29 18:22:49 -0700111 }
112
113 @NonNull
114 private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
Adam Hef27433a2020-03-25 15:11:18 -0700115 @Override
116 public void surfaceCreated(@NonNull SurfaceHolder holder) {
Svet Ganova5b49902020-06-18 00:47:22 -0700117 final SurfaceControl surfaceControl = mSurfaceView.getSurfaceControl();
118 surfaceControl.addOnReparentListener(mOnReparentListener);
119 mSurfaceControlCallback.onCreated(surfaceControl);
Adam Hef27433a2020-03-25 15:11:18 -0700120 }
121
122 @Override
Feng Caob46851c2020-04-29 18:22:49 -0700123 public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
124 int height) {
Adam Hef27433a2020-03-25 15:11:18 -0700125 /* do nothing */
126 }
127
128 @Override
129 public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
Svet Ganova5b49902020-06-18 00:47:22 -0700130 final SurfaceControl surfaceControl = mSurfaceView.getSurfaceControl();
131 surfaceControl.removeOnReparentListener(mOnReparentListener);
132 mSurfaceControlCallback.onDestroyed(surfaceControl);
133 }
134 };
135
136 @NonNull
137 private final SurfaceControl.OnReparentListener mOnReparentListener =
138 new SurfaceControl.OnReparentListener() {
139 @Override
140 public void onReparent(SurfaceControl.Transaction transaction,
141 SurfaceControl parent) {
142 final View parentSurfaceOwnerView = (parent != null)
143 ? parent.getLocalOwnerView() : null;
144 if (parentSurfaceOwnerView instanceof SurfaceView) {
145 mParentSurfaceOwnerView = new WeakReference<>(
146 (SurfaceView) parentSurfaceOwnerView);
147 } else {
148 mParentSurfaceOwnerView = null;
149 }
150 }
151 };
152
153 @NonNull
154 private final ViewTreeObserver.OnDrawListener mOnDrawListener =
155 new ViewTreeObserver.OnDrawListener() {
156 @Override
157 public void onDraw() {
158 computeParentPositionAndScale();
Qi (wangqi) Wangdb0dbea2020-06-24 19:42:20 -0700159 final int visibility = InlineContentView.this.isShown() ? VISIBLE : GONE;
160 mSurfaceView.setVisibility(visibility);
Adam Hef27433a2020-03-25 15:11:18 -0700161 }
162 };
163
Feng Caob46851c2020-04-29 18:22:49 -0700164 @NonNull
165 private final SurfaceView mSurfaceView;
Adam Hef27433a2020-03-25 15:11:18 -0700166
Feng Caob46851c2020-04-29 18:22:49 -0700167 @Nullable
Svet Ganova5b49902020-06-18 00:47:22 -0700168 private WeakReference<SurfaceView> mParentSurfaceOwnerView;
169
170 @Nullable
171 private int[] mParentPosition;
172
173 @Nullable
174 private PointF mParentScale;
175
176 @Nullable
Feng Caob46851c2020-04-29 18:22:49 -0700177 private SurfaceControlCallback mSurfaceControlCallback;
178
179 @Nullable
180 private SurfacePackageUpdater mSurfacePackageUpdater;
Adam Hef27433a2020-03-25 15:11:18 -0700181
182 /**
183 * @inheritDoc
Adam Hef27433a2020-03-25 15:11:18 -0700184 * @hide
185 */
186 public InlineContentView(@NonNull Context context) {
187 this(context, null);
188 }
189
190 /**
191 * @inheritDoc
Adam Hef27433a2020-03-25 15:11:18 -0700192 * @hide
193 */
194 public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs) {
195 this(context, attrs, 0);
196 }
197
198 /**
199 * @inheritDoc
Adam Hef27433a2020-03-25 15:11:18 -0700200 * @hide
201 */
202 public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
203 int defStyleAttr) {
204 this(context, attrs, defStyleAttr, 0);
Svet Ganov787ec3f2020-06-22 16:40:16 -0700205 mSurfaceView.setEnableSurfaceClipping(true);
Adam Hef27433a2020-03-25 15:11:18 -0700206 }
207
208 /**
Feng Caob46851c2020-04-29 18:22:49 -0700209 * Gets the surface control. If the surface is not created this method returns {@code null}.
Adam Hef27433a2020-03-25 15:11:18 -0700210 *
211 * @return The surface control.
Adam Hef27433a2020-03-25 15:11:18 -0700212 * @see #setSurfaceControlCallback(SurfaceControlCallback)
213 */
Feng Caob46851c2020-04-29 18:22:49 -0700214 @Nullable
215 public SurfaceControl getSurfaceControl() {
Adam Hef27433a2020-03-25 15:11:18 -0700216 return mSurfaceView.getSurfaceControl();
217 }
218
Svet Ganov787ec3f2020-06-22 16:40:16 -0700219 @Override
220 public void setClipBounds(Rect clipBounds) {
221 super.setClipBounds(clipBounds);
222 mSurfaceView.setClipBounds(clipBounds);
223 }
224
Adam Hef27433a2020-03-25 15:11:18 -0700225 /**
226 * @inheritDoc
Adam Hef27433a2020-03-25 15:11:18 -0700227 * @hide
228 */
229 public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
230 int defStyleAttr, int defStyleRes) {
231 super(context, attrs, defStyleAttr, defStyleRes);
Svet Ganova5b49902020-06-18 00:47:22 -0700232 mSurfaceView = new SurfaceView(context, attrs, defStyleAttr, defStyleRes) {
233 @Override
234 protected void onSetSurfacePositionAndScaleRT(
235 @NonNull SurfaceControl.Transaction transaction,
236 @NonNull SurfaceControl surface, int positionLeft, int positionTop,
237 float postScaleX, float postScaleY) {
238 // If we have a parent position, we need to make our coordinates relative
239 // to the parent in the rendering space.
240 if (mParentPosition != null) {
241 positionLeft = (int) ((positionLeft - mParentPosition[0]) / mParentScale.x);
242 positionTop = (int) ((positionTop - mParentPosition[1]) / mParentScale.y);
243 }
244
245 // Any scaling done to the parent or its predecessors would be applied
246 // via the surfaces parent -> child relation, so we only propagate any
247 // scaling set on the InlineContentView itself.
248 postScaleX = InlineContentView.this.getScaleX();
249 postScaleY = InlineContentView.this.getScaleY();
250
251 super.onSetSurfacePositionAndScaleRT(transaction, surface, positionLeft,
252 positionTop, postScaleX, postScaleY);
253 }
254 };
Adam Hef27433a2020-03-25 15:11:18 -0700255 mSurfaceView.setZOrderOnTop(true);
256 mSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
257 addView(mSurfaceView);
Svet Ganova5b49902020-06-18 00:47:22 -0700258 setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
Adam Hef27433a2020-03-25 15:11:18 -0700259 }
260
261 /**
Feng Caob46851c2020-04-29 18:22:49 -0700262 * Sets the embedded UI provider.
Adam Hef27433a2020-03-25 15:11:18 -0700263 *
264 * @hide
265 */
Svet Ganova5b49902020-06-18 00:47:22 -0700266 @TestApi
Feng Caob46851c2020-04-29 18:22:49 -0700267 public void setChildSurfacePackageUpdater(
268 @Nullable SurfacePackageUpdater surfacePackageUpdater) {
269 mSurfacePackageUpdater = surfacePackageUpdater;
270 }
271
272 @Override
273 protected void onAttachedToWindow() {
274 if (DEBUG) Log.v(TAG, "onAttachedToWindow");
275 super.onAttachedToWindow();
276 if (mSurfacePackageUpdater != null) {
277 mSurfacePackageUpdater.getSurfacePackage(
278 sp -> {
279 if (DEBUG) Log.v(TAG, "Received new SurfacePackage");
Feng Cao10e62172020-06-07 18:46:19 -0700280 if (getViewRootImpl() != null) {
281 mSurfaceView.setChildSurfacePackage(sp);
282 }
Feng Caob46851c2020-04-29 18:22:49 -0700283 });
284 }
Svet Ganova5b49902020-06-18 00:47:22 -0700285
286 mSurfaceView.setVisibility(getVisibility());
287 getViewTreeObserver().addOnDrawListener(mOnDrawListener);
Feng Caob46851c2020-04-29 18:22:49 -0700288 }
289
290 @Override
291 protected void onDetachedFromWindow() {
292 if (DEBUG) Log.v(TAG, "onDetachedFromWindow");
293 super.onDetachedFromWindow();
294 if (mSurfacePackageUpdater != null) {
295 mSurfacePackageUpdater.onSurfacePackageReleased();
296 }
Svet Ganova5b49902020-06-18 00:47:22 -0700297
298 getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
299 mSurfaceView.setVisibility(View.GONE);
Adam Hef27433a2020-03-25 15:11:18 -0700300 }
301
302 @Override
303 public void onLayout(boolean changed, int l, int t, int r, int b) {
Svet Ganov5baa2792020-03-29 22:56:19 -0700304 mSurfaceView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
Adam Hef27433a2020-03-25 15:11:18 -0700305 }
306
307 /**
Feng Caob46851c2020-04-29 18:22:49 -0700308 * Sets a callback to observe the lifecycle of the surface control for managing the backing
309 * surface.
Adam Hef27433a2020-03-25 15:11:18 -0700310 *
311 * @param callback The callback to set or {@code null} to clear.
312 */
313 public void setSurfaceControlCallback(@Nullable SurfaceControlCallback callback) {
314 if (mSurfaceControlCallback != null) {
315 mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
316 }
317 mSurfaceControlCallback = callback;
318 if (mSurfaceControlCallback != null) {
319 mSurfaceView.getHolder().addCallback(mSurfaceCallback);
320 }
321 }
322
323 /**
324 * @return Whether the surface backing this view appears on top of its parent.
Adam Hef27433a2020-03-25 15:11:18 -0700325 * @see #setZOrderedOnTop(boolean)
326 */
327 public boolean isZOrderedOnTop() {
328 return mSurfaceView.isZOrderedOnTop();
329 }
330
331 /**
Feng Caob46851c2020-04-29 18:22:49 -0700332 * Controls whether the backing surface is placed on top of this view's window. Normally, it is
333 * placed on top of the window, to allow interaction with the inlined UI. Via this method, you
334 * can place the surface below the window. This means that all of the contents of the window
335 * this view is in will be visible on top of its surface.
Adam Hef27433a2020-03-25 15:11:18 -0700336 *
337 * <p> The Z ordering can be changed dynamically if the backing surface is
338 * created, otherwise the ordering would be applied at surface construction time.
339 *
340 * @param onTop Whether to show the surface on top of this view's window.
Adam Hef27433a2020-03-25 15:11:18 -0700341 * @see #isZOrderedOnTop()
342 */
343 public boolean setZOrderedOnTop(boolean onTop) {
344 return mSurfaceView.setZOrderedOnTop(onTop, /*allowDynamicChange*/ true);
345 }
Svet Ganova5b49902020-06-18 00:47:22 -0700346
347
348 private void computeParentPositionAndScale() {
349 boolean contentPositionOrScaleChanged = false;
350
351 // This method can be called on the UI or render thread but for the cases
352 // it is called these threads are not running concurrently, so no need to lock.
353 final SurfaceView parentSurfaceOwnerView = (mParentSurfaceOwnerView != null)
354 ? mParentSurfaceOwnerView.get() : null;
355
356 if (parentSurfaceOwnerView != null) {
357 if (mParentPosition == null) {
358 mParentPosition = new int[2];
359 }
360 final int oldParentPositionX = mParentPosition[0];
361 final int oldParentPositionY = mParentPosition[1];
362 parentSurfaceOwnerView.getLocationInSurface(mParentPosition);
363 if (oldParentPositionX != mParentPosition[0]
364 || oldParentPositionY != mParentPosition[1]) {
365 contentPositionOrScaleChanged = true;
366 }
367
368 if (mParentScale == null) {
369 mParentScale = new PointF();
370 }
371
372 final float lastParentSurfaceWidth = parentSurfaceOwnerView
373 .getSurfaceRenderPosition().width();
374 final float oldParentScaleX = mParentScale.x;
375 if (lastParentSurfaceWidth > 0) {
376 mParentScale.x = lastParentSurfaceWidth /
377 (float) parentSurfaceOwnerView.getWidth();
378 } else {
379 mParentScale.x = 1.0f;
380 }
381 if (!contentPositionOrScaleChanged
382 && Float.compare(oldParentScaleX, mParentScale.x) != 0) {
383 contentPositionOrScaleChanged = true;
384 }
385
386 final float lastParentSurfaceHeight = parentSurfaceOwnerView
387 .getSurfaceRenderPosition().height();
388 final float oldParentScaleY = mParentScale.y;
389 if (lastParentSurfaceHeight > 0) {
390 mParentScale.y = lastParentSurfaceHeight
391 / (float) parentSurfaceOwnerView.getHeight();
392 } else {
393 mParentScale.y = 1.0f;
394 }
395 if (!contentPositionOrScaleChanged
396 && Float.compare(oldParentScaleY, mParentScale.y) != 0) {
397 contentPositionOrScaleChanged = true;
398 }
399 } else if (mParentPosition != null || mParentScale != null) {
400 contentPositionOrScaleChanged = true;
401 mParentPosition = null;
402 mParentScale = null;
403 }
404
405 if (contentPositionOrScaleChanged) {
406 mSurfaceView.requestUpdateSurfacePositionAndScale();
407 }
408 }
Adam Hef27433a2020-03-25 15:11:18 -0700409}