blob: 6a85de5ca75764e40852dbe50eefd7871c79a2dd [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;
21import android.content.Context;
22import android.graphics.PixelFormat;
23import android.util.AttributeSet;
Feng Caob46851c2020-04-29 18:22:49 -070024import android.util.Log;
Adam Hef27433a2020-03-25 15:11:18 -070025import android.view.SurfaceControl;
26import android.view.SurfaceControlViewHost;
27import android.view.SurfaceHolder;
28import android.view.SurfaceView;
29import android.view.ViewGroup;
30
Feng Caob46851c2020-04-29 18:22:49 -070031import java.util.function.Consumer;
32
Adam Hef27433a2020-03-25 15:11:18 -070033/**
Feng Caob46851c2020-04-29 18:22:49 -070034 * This class represents a view that holds opaque content from another app that you can inline in
35 * your UI.
Adam Hef27433a2020-03-25 15:11:18 -070036 *
37 * <p>Since the content presented by this view is from another security domain,it is
Feng Caob46851c2020-04-29 18:22:49 -070038 * shown on a remote surface preventing the host application from accessing that content. Also the
39 * host application cannot interact with the inlined content by injecting touch events or clicking
40 * programmatically.
Adam Hef27433a2020-03-25 15:11:18 -070041 *
42 * <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 -070043 * the inlined UI would not be interactive. Sometimes this is desirable, e.g. animating transitions.
Adam Hef27433a2020-03-25 15:11:18 -070044 *
45 * <p>By default the surface backing this view is shown on top of the hosting window such
Feng Caob46851c2020-04-29 18:22:49 -070046 * that the inlined content is interactive. However, you can temporarily move the surface under the
47 * hosting window which could be useful in some cases, e.g. animating transitions. At this point the
48 * inlined content will not be interactive and the touch events would be delivered to your app.
49 *
50 * <p> Instances of this class are created by the platform and can be programmatically attached to
51 * your UI. Once the view is attached to the window, you may detach and reattach it to the window.
52 * It should work seamlessly from the hosting process's point of view.
Adam Hef27433a2020-03-25 15:11:18 -070053 */
54public class InlineContentView extends ViewGroup {
55
Feng Caob46851c2020-04-29 18:22:49 -070056 private static final String TAG = "InlineContentView";
57
58 private static final boolean DEBUG = false;
59
Adam Hef27433a2020-03-25 15:11:18 -070060 /**
Feng Caob46851c2020-04-29 18:22:49 -070061 * Callback for observing the lifecycle of the surface control that manipulates the backing
62 * secure embedded UI surface.
Adam Hef27433a2020-03-25 15:11:18 -070063 */
64 public interface SurfaceControlCallback {
65 /**
66 * Called when the backing surface is being created.
67 *
68 * @param surfaceControl The surface control to manipulate the surface.
69 */
70 void onCreated(@NonNull SurfaceControl surfaceControl);
71
72 /**
73 * Called when the backing surface is being destroyed.
74 *
75 * @param surfaceControl The surface control to manipulate the surface.
76 */
77 void onDestroyed(@NonNull SurfaceControl surfaceControl);
78 }
79
Feng Caob46851c2020-04-29 18:22:49 -070080 /**
81 * Callback for sending an updated surface package in case the previous one is released
82 * from the detached from window event, and for getting notified of such event.
83 *
84 * This is expected to be provided to the {@link InlineContentView} so it can get updates
85 * from and send updates to the remote content (i.e. surface package) provider.
86 *
87 * @hide
88 */
89 public interface SurfacePackageUpdater {
90
91 /**
92 * Called when the previous surface package is released due to view being detached
93 * from the window.
94 */
95 void onSurfacePackageReleased();
96
97 /**
98 * Called to request an updated surface package.
99 *
100 * @param consumer consumes the updated surface package.
101 */
102 void getSurfacePackage(Consumer<SurfaceControlViewHost.SurfacePackage> consumer);
103 }
104
105 @NonNull
106 private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
Adam Hef27433a2020-03-25 15:11:18 -0700107 @Override
108 public void surfaceCreated(@NonNull SurfaceHolder holder) {
109 mSurfaceControlCallback.onCreated(mSurfaceView.getSurfaceControl());
110 }
111
112 @Override
Feng Caob46851c2020-04-29 18:22:49 -0700113 public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
114 int height) {
Adam Hef27433a2020-03-25 15:11:18 -0700115 /* do nothing */
116 }
117
118 @Override
119 public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
120 mSurfaceControlCallback.onDestroyed(mSurfaceView.getSurfaceControl());
121 }
122 };
123
Feng Caob46851c2020-04-29 18:22:49 -0700124 @NonNull
125 private final SurfaceView mSurfaceView;
Adam Hef27433a2020-03-25 15:11:18 -0700126
Feng Caob46851c2020-04-29 18:22:49 -0700127 @Nullable
128 private SurfaceControlCallback mSurfaceControlCallback;
129
130 @Nullable
131 private SurfacePackageUpdater mSurfacePackageUpdater;
Adam Hef27433a2020-03-25 15:11:18 -0700132
133 /**
134 * @inheritDoc
Adam Hef27433a2020-03-25 15:11:18 -0700135 * @hide
136 */
137 public InlineContentView(@NonNull Context context) {
138 this(context, null);
139 }
140
141 /**
142 * @inheritDoc
Adam Hef27433a2020-03-25 15:11:18 -0700143 * @hide
144 */
145 public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs) {
146 this(context, attrs, 0);
147 }
148
149 /**
150 * @inheritDoc
Adam Hef27433a2020-03-25 15:11:18 -0700151 * @hide
152 */
153 public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
154 int defStyleAttr) {
155 this(context, attrs, defStyleAttr, 0);
156 }
157
158 /**
Feng Caob46851c2020-04-29 18:22:49 -0700159 * Gets the surface control. If the surface is not created this method returns {@code null}.
Adam Hef27433a2020-03-25 15:11:18 -0700160 *
161 * @return The surface control.
Adam Hef27433a2020-03-25 15:11:18 -0700162 * @see #setSurfaceControlCallback(SurfaceControlCallback)
163 */
Feng Caob46851c2020-04-29 18:22:49 -0700164 @Nullable
165 public SurfaceControl getSurfaceControl() {
Adam Hef27433a2020-03-25 15:11:18 -0700166 return mSurfaceView.getSurfaceControl();
167 }
168
169 /**
170 * @inheritDoc
Adam Hef27433a2020-03-25 15:11:18 -0700171 * @hide
172 */
173 public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
174 int defStyleAttr, int defStyleRes) {
175 super(context, attrs, defStyleAttr, defStyleRes);
176 mSurfaceView = new SurfaceView(context, attrs, defStyleAttr, defStyleRes);
177 mSurfaceView.setZOrderOnTop(true);
178 mSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
179 addView(mSurfaceView);
180 }
181
182 /**
Feng Caob46851c2020-04-29 18:22:49 -0700183 * Sets the embedded UI provider.
Adam Hef27433a2020-03-25 15:11:18 -0700184 *
185 * @hide
186 */
Feng Caob46851c2020-04-29 18:22:49 -0700187 public void setChildSurfacePackageUpdater(
188 @Nullable SurfacePackageUpdater surfacePackageUpdater) {
189 mSurfacePackageUpdater = surfacePackageUpdater;
190 }
191
192 @Override
193 protected void onAttachedToWindow() {
194 if (DEBUG) Log.v(TAG, "onAttachedToWindow");
195 super.onAttachedToWindow();
196 if (mSurfacePackageUpdater != null) {
197 mSurfacePackageUpdater.getSurfacePackage(
198 sp -> {
199 if (DEBUG) Log.v(TAG, "Received new SurfacePackage");
Feng Cao10e62172020-06-07 18:46:19 -0700200 if (getViewRootImpl() != null) {
201 mSurfaceView.setChildSurfacePackage(sp);
202 }
Feng Caob46851c2020-04-29 18:22:49 -0700203 });
204 }
205 }
206
207 @Override
208 protected void onDetachedFromWindow() {
209 if (DEBUG) Log.v(TAG, "onDetachedFromWindow");
210 super.onDetachedFromWindow();
211 if (mSurfacePackageUpdater != null) {
212 mSurfacePackageUpdater.onSurfacePackageReleased();
213 }
Adam Hef27433a2020-03-25 15:11:18 -0700214 }
215
216 @Override
217 public void onLayout(boolean changed, int l, int t, int r, int b) {
Svet Ganov5baa2792020-03-29 22:56:19 -0700218 mSurfaceView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
Adam Hef27433a2020-03-25 15:11:18 -0700219 }
220
221 /**
Feng Caob46851c2020-04-29 18:22:49 -0700222 * Sets a callback to observe the lifecycle of the surface control for managing the backing
223 * surface.
Adam Hef27433a2020-03-25 15:11:18 -0700224 *
225 * @param callback The callback to set or {@code null} to clear.
226 */
227 public void setSurfaceControlCallback(@Nullable SurfaceControlCallback callback) {
228 if (mSurfaceControlCallback != null) {
229 mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
230 }
231 mSurfaceControlCallback = callback;
232 if (mSurfaceControlCallback != null) {
233 mSurfaceView.getHolder().addCallback(mSurfaceCallback);
234 }
235 }
236
237 /**
238 * @return Whether the surface backing this view appears on top of its parent.
Adam Hef27433a2020-03-25 15:11:18 -0700239 * @see #setZOrderedOnTop(boolean)
240 */
241 public boolean isZOrderedOnTop() {
242 return mSurfaceView.isZOrderedOnTop();
243 }
244
245 /**
Feng Caob46851c2020-04-29 18:22:49 -0700246 * Controls whether the backing surface is placed on top of this view's window. Normally, it is
247 * placed on top of the window, to allow interaction with the inlined UI. Via this method, you
248 * can place the surface below the window. This means that all of the contents of the window
249 * this view is in will be visible on top of its surface.
Adam Hef27433a2020-03-25 15:11:18 -0700250 *
251 * <p> The Z ordering can be changed dynamically if the backing surface is
252 * created, otherwise the ordering would be applied at surface construction time.
253 *
254 * @param onTop Whether to show the surface on top of this view's window.
Adam Hef27433a2020-03-25 15:11:18 -0700255 * @see #isZOrderedOnTop()
256 */
257 public boolean setZOrderedOnTop(boolean onTop) {
258 return mSurfaceView.setZOrderedOnTop(onTop, /*allowDynamicChange*/ true);
259 }
260}