ContentViewRenderView.java revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser;
6
7import android.content.Context;
8import android.graphics.Color;
9import android.graphics.PixelFormat;
10import android.view.Surface;
11import android.view.SurfaceHolder;
12import android.view.SurfaceView;
13import android.widget.FrameLayout;
14
15import org.chromium.base.CalledByNative;
16import org.chromium.base.JNINamespace;
17import org.chromium.ui.base.WindowAndroid;
18
19import java.util.ArrayList;
20import java.util.List;
21
22/***
23 * This view is used by a ContentView to render its content.
24 * Call {@link #setCurrentContentViewCore(ContentViewCore)} with the contentViewCore that should be
25 * managing the view.
26 * Note that only one ContentViewCore can be shown at a time.
27 */
28@JNINamespace("content")
29public class ContentViewRenderView extends FrameLayout {
30    // The native side of this object.
31    private long mNativeContentViewRenderView;
32    private SurfaceHolder.Callback mSurfaceCallback;
33    private List<DelayedSurfaceRunnable> mDelayedSurfaceRunnableList;
34
35    private final SurfaceView mSurfaceView;
36    protected ContentViewCore mContentViewCore;
37
38    private ContentReadbackHandler mContentReadbackHandler;
39
40    /**
41     * Constructing the SurfaceView early sends surface created notifications
42     * before the native library is loaded. This runnable sends the same signals after
43     * the library is loaded.
44     */
45    private class DelayedSurfaceRunnable implements Runnable {
46        private final SurfaceHolder mHolder;
47        private final int mFormat;
48        private final int mWidth;
49        private final int mHeight;
50
51        /**
52         * see https://developer.android.com/reference/android/view/SurfaceHolder.Callback.html#
53         * surfaceChanged(android.view.SurfaceHolder, int, int, int)
54         */
55        public DelayedSurfaceRunnable(SurfaceHolder holder, int format, int width, int height) {
56            mHolder = holder;
57            mFormat = format;
58            mWidth = width;
59            mHeight = height;
60        }
61
62        @Override
63        public void run() {
64            assert mNativeContentViewRenderView != 0;
65            nativeSurfaceChanged(mNativeContentViewRenderView, mFormat, mWidth, mHeight,
66                    mHolder.getSurface());
67            if (mContentViewCore != null) {
68                mContentViewCore.onPhysicalBackingSizeChanged(mWidth, mHeight);
69            }
70        }
71    }
72
73    /**
74     * Constructs a new ContentViewRenderView.
75     * This should be called and the {@link ContentViewRenderView} should be added to the view
76     * hierarchy before the first draw to avoid a black flash that is seen every time a
77     * {@link SurfaceView} is added.
78     * @param context The context used to create this.
79     */
80    public ContentViewRenderView(Context context) {
81        super(context);
82
83        mSurfaceView = createSurfaceView(getContext());
84        mSurfaceView.setZOrderMediaOverlay(true);
85
86        setSurfaceViewBackgroundColor(Color.WHITE);
87
88        // Add a placeholder callback which will keep track of the last surfaceChanged call if we
89        // get any until the native libraries have been loaded.
90        mSurfaceCallback = new SurfaceHolder.Callback() {
91            @Override
92            public void surfaceDestroyed(SurfaceHolder holder) {
93                mDelayedSurfaceRunnableList = null;
94            }
95
96            @Override
97            public void surfaceCreated(SurfaceHolder holder) {
98                mDelayedSurfaceRunnableList =
99                        new ArrayList<ContentViewRenderView.DelayedSurfaceRunnable>();
100            }
101
102            @Override
103            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
104                mDelayedSurfaceRunnableList.add(
105                        new DelayedSurfaceRunnable(holder, format, width, height));
106                return;
107            }
108        };
109        mSurfaceView.getHolder().addCallback(mSurfaceCallback);
110
111        addView(mSurfaceView,
112                new FrameLayout.LayoutParams(
113                        FrameLayout.LayoutParams.MATCH_PARENT,
114                        FrameLayout.LayoutParams.MATCH_PARENT));
115    }
116
117    /**
118     * Initialization that requires native libraries should be done here.
119     * Native code should add/remove the layers to be rendered through the ContentViewLayerRenderer.
120     * @param rootWindow The {@link WindowAndroid} this render view should be linked to.
121     */
122    public void onNativeLibraryLoaded(WindowAndroid rootWindow) {
123        assert rootWindow != null;
124        mNativeContentViewRenderView = nativeInit(rootWindow.getNativePointer());
125        assert mNativeContentViewRenderView != 0;
126        mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
127        mSurfaceCallback = new SurfaceHolder.Callback() {
128            @Override
129            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
130                assert mNativeContentViewRenderView != 0;
131                nativeSurfaceChanged(mNativeContentViewRenderView,
132                        format, width, height, holder.getSurface());
133                if (mContentViewCore != null) {
134                    mContentViewCore.onPhysicalBackingSizeChanged(
135                            width, height);
136                }
137            }
138
139            @Override
140            public void surfaceCreated(SurfaceHolder holder) {
141                assert mNativeContentViewRenderView != 0;
142                nativeSurfaceCreated(mNativeContentViewRenderView);
143
144                onReadyToRender();
145            }
146
147            @Override
148            public void surfaceDestroyed(SurfaceHolder holder) {
149                assert mNativeContentViewRenderView != 0;
150                nativeSurfaceDestroyed(mNativeContentViewRenderView);
151            }
152        };
153        mSurfaceView.getHolder().addCallback(mSurfaceCallback);
154
155        mContentReadbackHandler = new ContentReadbackHandler() {
156            @Override
157            protected boolean readyForReadback() {
158                return mNativeContentViewRenderView != 0 && mContentViewCore != null;
159            }
160        };
161        mContentReadbackHandler.initNativeContentReadbackHandler();
162        if (mDelayedSurfaceRunnableList != null) {
163            nativeSurfaceCreated(mNativeContentViewRenderView);
164            for (int i = 0; i < mDelayedSurfaceRunnableList.size(); i++) {
165                mDelayedSurfaceRunnableList.get(i).run();
166            }
167            mDelayedSurfaceRunnableList = null;
168        }
169    }
170
171    /**
172     * @return The content readback handler.
173     */
174    public ContentReadbackHandler getContentReadbackHandler() {
175        return mContentReadbackHandler;
176    }
177
178    /**
179     * Sets the background color of the surface view.  This method is necessary because the
180     * background color of ContentViewRenderView itself is covered by the background of
181     * SurfaceView.
182     * @param color The color of the background.
183     */
184    public void setSurfaceViewBackgroundColor(int color) {
185        if (mSurfaceView != null) {
186            mSurfaceView.setBackgroundColor(color);
187        }
188    }
189
190    /**
191     * Should be called when the ContentViewRenderView is not needed anymore so its associated
192     * native resource can be freed.
193     */
194    public void destroy() {
195        mContentReadbackHandler.destroy();
196        mContentReadbackHandler = null;
197        mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
198        nativeDestroy(mNativeContentViewRenderView);
199        mNativeContentViewRenderView = 0;
200    }
201
202    public void setCurrentContentViewCore(ContentViewCore contentViewCore) {
203        assert mNativeContentViewRenderView != 0;
204        mContentViewCore = contentViewCore;
205
206        if (mContentViewCore != null) {
207            mContentViewCore.onPhysicalBackingSizeChanged(getWidth(), getHeight());
208            nativeSetCurrentContentViewCore(mNativeContentViewRenderView,
209                                            mContentViewCore.getNativeContentViewCore());
210        } else {
211            nativeSetCurrentContentViewCore(mNativeContentViewRenderView, 0);
212        }
213    }
214
215    /**
216     * This method should be subclassed to provide actions to be performed once the view is ready to
217     * render.
218     */
219    protected void onReadyToRender() {
220    }
221
222    /**
223     * This method could be subclassed optionally to provide a custom SurfaceView object to
224     * this ContentViewRenderView.
225     * @param context The context used to create the SurfaceView object.
226     * @return The created SurfaceView object.
227     */
228    protected SurfaceView createSurfaceView(Context context) {
229        return new SurfaceView(context);
230    }
231
232    /**
233     * @return whether the surface view is initialized and ready to render.
234     */
235    public boolean isInitialized() {
236        return mSurfaceView.getHolder().getSurface() != null;
237    }
238
239    /**
240     * Enter or leave overlay video mode.
241     * @param enabled Whether overlay mode is enabled.
242     */
243    public void setOverlayVideoMode(boolean enabled) {
244        int format = enabled ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
245        mSurfaceView.getHolder().setFormat(format);
246        nativeSetOverlayVideoMode(mNativeContentViewRenderView, enabled);
247    }
248
249    /**
250     * Set the native layer tree helper for this {@link ContentViewRenderView}.
251     * @param layerTreeBuildHelperNativePtr Native pointer to the layer tree build helper.
252     */
253    public void setLayerTreeBuildHelper(long layerTreeBuildHelperNativePtr) {
254        nativeSetLayerTreeBuildHelper(mNativeContentViewRenderView, layerTreeBuildHelperNativePtr);
255    }
256
257    @CalledByNative
258    protected void onCompositorLayout() {
259    }
260
261    @CalledByNative
262    private void onSwapBuffersCompleted() {
263        if (mSurfaceView.getBackground() != null) {
264            post(new Runnable() {
265                @Override public void run() {
266                    mSurfaceView.setBackgroundResource(0);
267                }
268            });
269        }
270    }
271
272    private native long nativeInit(long rootWindowNativePointer);
273    private native void nativeDestroy(long nativeContentViewRenderView);
274    private native void nativeSetCurrentContentViewCore(long nativeContentViewRenderView,
275            long nativeContentViewCore);
276    private native void nativeSetLayerTreeBuildHelper(long nativeContentViewRenderView,
277            long buildHelperNativePtr);
278    private native void nativeSurfaceCreated(long nativeContentViewRenderView);
279    private native void nativeSurfaceDestroyed(long nativeContentViewRenderView);
280    private native void nativeSurfaceChanged(long nativeContentViewRenderView,
281            int format, int width, int height, Surface surface);
282    private native void nativeSetOverlayVideoMode(long nativeContentViewRenderView,
283            boolean enabled);
284}
285