// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chromecast.shell;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.os.IBinder;
import android.widget.FrameLayout;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import org.chromium.base.supplier.Supplier;
import org.chromium.chromecast.base.Observer;
import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.components.embedder_support.view.ContentViewRenderView;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.ActivityWindowAndroid;
import org.chromium.ui.base.IntentRequestTracker;
import org.chromium.ui.base.WindowAndroid;
class CastWebContentsScopes {
interface WindowTokenProvider {
@Nullable
IBinder provideWindowToken();
}
static final String VIEW_TAG_CONTENT_VIEW = "ContentView";
public static Observer<WebContents> onLayoutActivity(
Activity activity, FrameLayout layout, @ColorInt int backgroundColor) {
layout.setBackgroundColor(backgroundColor);
return onLayoutInternal(activity, layout, () -> {
return new ActivityWindowAndroid(activity, /* listenToActivityState= */ true,
IntentRequestTracker.createFromActivity(activity));
}, backgroundColor);
}
public static Observer<WebContents> onLayoutFragment(
Activity activity, FrameLayout layout, @ColorInt int backgroundColor) {
layout.setBackgroundColor(backgroundColor);
return onLayoutInternal(
activity, layout, () -> new WindowAndroid(activity), backgroundColor);
}
static Observer<WebContents> onLayoutView(Context context, FrameLayout layout,
@ColorInt int backgroundColor, WindowTokenProvider windowTokenProvider) {
layout.setBackgroundColor(backgroundColor);
return onLayoutInternal(context, layout, () -> new WindowAndroid(context) {
@Override
public IBinder getWindowToken() {
return windowTokenProvider.provideWindowToken();
}
}, backgroundColor);
}
// Note: the |windowFactory| should create a new instance of a WindowAndroid each time it is
// invoked.
private static Observer<WebContents> onLayoutInternal(Context context, FrameLayout layout,
Supplier<WindowAndroid> windowFactory, @ColorInt int backgroundColor) {
return (WebContents webContents) -> {
WindowAndroid window = windowFactory.get();
ContentViewRenderView contentViewRenderView =
new ContentViewRenderView(context) {
@Override
protected void onReadyToRender() {
setOverlayVideoMode(true);
}
};
contentViewRenderView.onNativeLibraryLoaded(window);
contentViewRenderView.setSurfaceViewBackgroundColor(backgroundColor);
FrameLayout.LayoutParams matchParent =
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
// Use a slightly smaller layout as a mitigation for b/245596038 until the
// Android-level fix is available.
FrameLayout.LayoutParams talkbackFixLayout = new FrameLayout.LayoutParams(matchParent);
talkbackFixLayout.setMargins(0, 0, 1, 1);
layout.addView(contentViewRenderView, talkbackFixLayout);
ContentView contentView = ContentView.createContentView(context, webContents);
WebContentsRegistry.initializeWebContents(webContents, contentView, window);
// Enable display of current webContents.
webContents.onShow();
layout.addView(contentView, matchParent);
// Ensure that the foreground doesn't interfere with accessibility overlays.
layout.setForeground(null);
contentView.setFocusable(true);
contentView.requestFocus();
contentView.setTag(VIEW_TAG_CONTENT_VIEW);
contentViewRenderView.setCurrentWebContents(webContents);
return () -> {
layout.setForeground(new ColorDrawable(backgroundColor));
layout.removeView(contentView);
layout.removeView(contentViewRenderView);
webContents.setTopLevelNativeWindow(null);
contentViewRenderView.destroy();
window.destroy();
};
};
}
public static Observer<WebContents> withoutLayout(Context context) {
return (WebContents webContents) -> {
WindowAndroid window = new WindowAndroid(context);
ContentView contentView = ContentView.createContentView(context, webContents);
WebContentsRegistry.initializeWebContents(webContents, contentView, window);
// Enable display of current webContents.
webContents.onShow();
return () -> {
if (!webContents.isDestroyed()) {
// WebContents can be destroyed by the app before CastWebContentsComponent
// unbinds, which is why we need this check.
webContents.onHide();
if (webContents.getTopLevelNativeWindow() == window) {
webContents.setTopLevelNativeWindow(null);
}
}
window.destroy();
};
};
}
}