Skip to content

Commit abfea0c

Browse files
committed
cobalt: Use activity Window surface for UI rendering
Introduces support for rendering UI directly into the Activity's Window surface via Window.takeSurface(), as an alternative to an embedded child SurfaceView. This behavior is abstracted behind WindowSurfaceBridge and is controlled by the `--use-window-surface-for-ui` command-line flag (disabled by default). Issue: 494590075
1 parent 1d34043 commit abfea0c

2 files changed

Lines changed: 144 additions & 34 deletions

File tree

cobalt/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,9 @@ protected void createContent(final Bundle savedInstanceState) {
237237
// Set up the animation placeholder to be the SurfaceView. This disables the
238238
// SurfaceView's 'hole' clipping during animations that are notified to the window.
239239
mWindowAndroid.setAnimationPlaceholderView(
240-
mShellManager.getContentViewRenderView().getSurfaceView());
240+
mShellManager.getContentViewRenderView().getAnchorView());
241241
mA11yHelper =
242-
new CobaltA11yHelper(this, mShellManager.getContentViewRenderView().getSurfaceView());
242+
new CobaltA11yHelper(this, mShellManager.getContentViewRenderView().getAnchorView());
243243

244244
if (mStartupUrl == null || mStartupUrl.isEmpty()) {
245245
String[] args = getStarboardBridge().getArgs();

cobalt/shell/android/java/src/dev/cobalt/shell/ContentViewRenderView.java

Lines changed: 142 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import android.view.View;
1414
import android.widget.FrameLayout;
1515

16+
import org.chromium.base.CommandLine;
17+
import org.chromium.base.Log;
1618
import org.chromium.content_public.browser.WebContents;
1719
import org.chromium.ui.base.EventForwarder;
1820
import org.chromium.ui.base.WindowAndroid;
@@ -28,6 +30,8 @@
2830
*/
2931
@JNINamespace("cobalt")
3032
public class ContentViewRenderView extends FrameLayout {
33+
private static final String TAG = "cobalt";
34+
3135
// The native side of this object.
3236
private long mNativeContentViewRenderView;
3337
private WindowAndroid mWindowAndroid;
@@ -53,7 +57,12 @@ public ContentViewRenderView(Context context) {
5357
}
5458

5559
protected SurfaceBridge createSurfaceBridge() {
56-
return new SurfaceBridge();
60+
if (CommandLine.getInstance().hasSwitch("use-window-surface-for-ui")) {
61+
Log.i(TAG, "ContentViewRenderView is created using WindowSurfaceBridge");
62+
return new WindowSurfaceBridge();
63+
}
64+
Log.i(TAG, "ContentViewRenderView is created with SurfaceView");
65+
return new SurfaceViewBridge();
5766
}
5867

5968
/**
@@ -62,7 +71,8 @@ protected SurfaceBridge createSurfaceBridge() {
6271
* @param rootWindow The {@link WindowAndroid} this render view should be linked to.
6372
*/
6473
public void onNativeLibraryLoaded(WindowAndroid rootWindow) {
65-
assert !getSurfaceView().getHolder().getSurface().isValid()
74+
assert mSurfaceBridge.getSurfaceView() == null
75+
|| !mSurfaceBridge.getSurfaceView().getHolder().getSurface().isValid()
6676
: "Surface created before native library loaded.";
6777
assert rootWindow != null;
6878
mNativeContentViewRenderView =
@@ -96,7 +106,10 @@ public void surfaceCreated(SurfaceHolder holder) {
96106
// devices, where a relayout never happens. This bug is out of Chromium's
97107
// control, but can be worked around by forcibly re-setting the visibility of
98108
// the surface view. Otherwise, the screen stays black, and some tests fail.
99-
getSurfaceView().setVisibility(getSurfaceView().getVisibility());
109+
SurfaceView surfaceView = mSurfaceBridge.getSurfaceView();
110+
if (surfaceView != null) {
111+
surfaceView.setVisibility(surfaceView.getVisibility());
112+
}
100113

101114
onReadyToRender();
102115
}
@@ -108,7 +121,7 @@ public void surfaceDestroyed(SurfaceHolder holder) {
108121
mNativeContentViewRenderView, ContentViewRenderView.this);
109122
}
110123
};
111-
mSurfaceBridge.connect(surfaceCallback);
124+
mSurfaceBridge.connect(surfaceCallback, rootWindow);
112125
}
113126

114127
@Override
@@ -135,22 +148,12 @@ protected void onWindowVisibilityChanged(int visibility) {
135148
}
136149

137150
/**
138-
* Sets the background color of the surface view. This method is necessary because the
139-
* background color of ContentViewRenderView itself is covered by the background of
140-
* SurfaceView.
141-
* @param color The color of the background.
151+
* Gets the View used for layout anchoring, animation placeholder, or accessibility (child
152+
* SurfaceView in SurfaceView mode, or this host View in Window Surface mode).
142153
*/
143-
public void setSurfaceViewBackgroundColor(int color) {
144-
if (getSurfaceView() != null) {
145-
getSurfaceView().setBackgroundColor(color);
146-
}
147-
}
148-
149-
/**
150-
* Gets the SurfaceView for this ContentViewRenderView
151-
*/
152-
public SurfaceView getSurfaceView() {
153-
return mSurfaceBridge.getSurfaceView();
154+
public View getAnchorView() {
155+
SurfaceView surfaceView = mSurfaceBridge.getSurfaceView();
156+
return surfaceView != null ? surfaceView : this;
154157
}
155158

156159
/**
@@ -195,47 +198,72 @@ protected SurfaceView createSurfaceView(Context context) {
195198
return new SurfaceView(context);
196199
}
197200

198-
/**
199-
* @return whether the surface view is initialized and ready to render.
200-
*/
201-
public boolean isInitialized() {
202-
return getSurfaceView().getHolder().getSurface() != null;
203-
}
204-
205201
/**
206202
* Enter or leave overlay video mode.
207203
* @param enabled Whether overlay mode is enabled.
208204
*/
209205
public void setOverlayVideoMode(boolean enabled) {
210206
int format = enabled ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
211-
getSurfaceView().getHolder().setFormat(format);
207+
mSurfaceBridge.setFormat(format);
212208
ContentViewRenderViewJni.get().setOverlayVideoMode(
213209
mNativeContentViewRenderView, ContentViewRenderView.this, enabled);
214210
}
215211

216212
@CalledByNative
217213
private void didSwapFrame() {
218-
if (getSurfaceView().getBackground() != null) {
214+
SurfaceView surfaceView = mSurfaceBridge.getSurfaceView();
215+
if (surfaceView == null) {
216+
// In Window Surface mode, no child SurfaceView background to clear.
217+
return;
218+
}
219+
220+
if (surfaceView.getBackground() != null) {
219221
post(new Runnable() {
220222
@Override
221223
public void run() {
222-
getSurfaceView().setBackgroundResource(0);
224+
surfaceView.setBackgroundResource(0);
223225
}
224226
});
225227
}
226228
}
227229

228230
/**
229-
* Connecting class to hold a SurfaceView.
231+
* Connecting class to hold a surface management strategy.
230232
*/
231-
protected static class SurfaceBridge {
233+
protected abstract static class SurfaceBridge {
234+
protected abstract void initialize(ContentViewRenderView renderView);
235+
protected abstract void connect(SurfaceHolder.Callback surfaceCallback, WindowAndroid windowAndroid);
236+
protected abstract void disconnect();
237+
protected abstract SurfaceView getSurfaceView();
238+
protected abstract void setFormat(int format);
239+
}
240+
241+
/**
242+
* SurfaceBridge implementation that uses a standard SurfaceView.
243+
* This is used for the default rendering path where a child SurfaceView is embedded
244+
* within the ContentViewRenderView.
245+
*
246+
* Lifetime: Bound to the lifetime of the outer ContentViewRenderView.
247+
* Threading: Must be called on the UI thread.
248+
*/
249+
protected static class SurfaceViewBridge extends SurfaceBridge {
232250
private SurfaceView mSurfaceView;
233251
private SurfaceHolder.Callback mSurfaceCallback;
234252

253+
@Override
235254
protected SurfaceView getSurfaceView() {
236255
return mSurfaceView;
237256
}
238257

258+
@Override
259+
protected void setFormat(int format) {
260+
if (mSurfaceView == null) {
261+
return;
262+
}
263+
mSurfaceView.getHolder().setFormat(format);
264+
}
265+
266+
@Override
239267
protected void initialize(ContentViewRenderView renderView) {
240268
mSurfaceView = renderView.createSurfaceView(renderView.getContext());
241269
mSurfaceView.setZOrderMediaOverlay(true);
@@ -246,17 +274,99 @@ protected void initialize(ContentViewRenderView renderView) {
246274
mSurfaceView.setVisibility(GONE);
247275
}
248276

249-
protected void connect(SurfaceHolder.Callback surfaceCallback) {
277+
@Override
278+
protected void connect(SurfaceHolder.Callback surfaceCallback, WindowAndroid windowAndroid) {
250279
mSurfaceCallback = surfaceCallback;
251280
mSurfaceView.getHolder().addCallback(mSurfaceCallback);
252281
mSurfaceView.setVisibility(VISIBLE);
253282
}
254283

284+
@Override
255285
protected void disconnect() {
256286
mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
257287
}
258288
}
259289

290+
/**
291+
* SurfaceBridge implementation that takes ownership of the Activity's Window surface.
292+
* This allows direct rendering to the window surface instead of a child SurfaceView.
293+
*
294+
* Lifetime: Bound to the lifetime of the outer ContentViewRenderView and the associated Activity.
295+
* Threading: Must be called on the UI thread.
296+
*/
297+
protected static class WindowSurfaceBridge extends SurfaceBridge {
298+
private SurfaceHolder mWindowSurfaceHolder;
299+
private Integer mSurfaceFormat;
300+
301+
@Override
302+
protected void initialize(ContentViewRenderView renderView) {}
303+
304+
@Override
305+
protected void connect(
306+
SurfaceHolder.Callback surfaceCallback, WindowAndroid windowAndroid) {
307+
if (windowAndroid == null) {
308+
return;
309+
}
310+
android.app.Activity activity = windowAndroid.getActivity().get();
311+
if (activity == null || activity.getWindow() == null) {
312+
return;
313+
}
314+
315+
activity.getWindow().takeSurface(new SurfaceHolder.Callback2() {
316+
@Override
317+
public void surfaceCreated(SurfaceHolder holder) {
318+
mWindowSurfaceHolder = holder;
319+
if (mSurfaceFormat != null) {
320+
Log.i(TAG, "Applying pending format");
321+
mWindowSurfaceHolder.setFormat(mSurfaceFormat);
322+
}
323+
surfaceCallback.surfaceCreated(holder);
324+
}
325+
326+
@Override
327+
public void surfaceChanged(
328+
SurfaceHolder holder, int format, int width, int height) {
329+
mWindowSurfaceHolder = holder;
330+
surfaceCallback.surfaceChanged(holder, format, width, height);
331+
}
332+
333+
@Override
334+
public void surfaceDestroyed(SurfaceHolder holder) {
335+
mWindowSurfaceHolder = null;
336+
surfaceCallback.surfaceDestroyed(holder);
337+
}
338+
339+
@Override
340+
public void surfaceRedrawNeeded(SurfaceHolder holder) {
341+
if (surfaceCallback instanceof SurfaceHolder.Callback2) {
342+
((SurfaceHolder.Callback2) surfaceCallback).surfaceRedrawNeeded(holder);
343+
}
344+
}
345+
});
346+
}
347+
348+
@Override
349+
protected void disconnect() {
350+
mWindowSurfaceHolder = null;
351+
mSurfaceFormat = null;
352+
}
353+
354+
@Override
355+
protected SurfaceView getSurfaceView() {
356+
return null;
357+
}
358+
359+
@Override
360+
protected void setFormat(int format) {
361+
mSurfaceFormat = format;
362+
if (mWindowSurfaceHolder == null) {
363+
Log.i(TAG, "Window surface is not ready yet. Will apply format later");
364+
return;
365+
}
366+
mWindowSurfaceHolder.setFormat(format);
367+
}
368+
}
369+
260370
private EventForwarder getEventForwarder() {
261371
if (mWebContents == null || mWebContents.isDestroyed()) {
262372
return null;

0 commit comments

Comments
 (0)