1 | // Copyright (c) 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 | |
5 | package org.chromium.content.browser; |
6 | |
7 | import android.content.Context; |
8 | import android.os.Build; |
9 | import android.view.Surface; |
10 | import android.view.SurfaceView; |
11 | import android.view.SurfaceHolder; |
12 | import android.widget.FrameLayout; |
13 | |
14 | import org.chromium.base.JNINamespace; |
15 | |
16 | /*** |
17 | * This view is used by a ContentView to render its content. |
18 | * Call {@link #setCurrentContentView(ContentView)} with the contentView that should be displayed. |
19 | * Note that only one ContentView can be shown at a time. |
20 | */ |
21 | @JNINamespace("content") |
22 | public class ContentViewRenderView extends FrameLayout { |
23 | |
24 | // The native side of this object. |
25 | private int mNativeContentViewRenderView = 0; |
26 | private final SurfaceHolder.Callback mSurfaceCallback; |
27 | |
28 | private SurfaceView mSurfaceView; |
29 | private VSyncAdapter mVSyncAdapter; |
30 | |
31 | private ContentView mCurrentContentView; |
32 | |
33 | /** |
34 | * Constructs a new ContentViewRenderView that should be can to a view hierarchy. |
35 | * Native code should add/remove the layers to be rendered through the ContentViewLayerRenderer. |
36 | * @param context The context used to create this. |
37 | */ |
38 | public ContentViewRenderView(Context context) { |
39 | super(context); |
40 | |
41 | mNativeContentViewRenderView = nativeInit(); |
42 | assert mNativeContentViewRenderView != 0; |
43 | |
44 | mSurfaceView = createSurfaceView(getContext()); |
45 | mSurfaceCallback = new SurfaceHolder.Callback() { |
46 | @Override |
47 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { |
48 | assert mNativeContentViewRenderView != 0; |
49 | nativeSurfaceSetSize(mNativeContentViewRenderView, width, height); |
50 | if (mCurrentContentView != null) { |
51 | mCurrentContentView.getContentViewCore().onPhysicalBackingSizeChanged( |
52 | width, height); |
53 | } |
54 | } |
55 | |
56 | @Override |
57 | public void surfaceCreated(SurfaceHolder holder) { |
58 | assert mNativeContentViewRenderView != 0; |
59 | nativeSurfaceCreated(mNativeContentViewRenderView, holder.getSurface()); |
60 | onReadyToRender(); |
61 | } |
62 | |
63 | @Override |
64 | public void surfaceDestroyed(SurfaceHolder holder) { |
65 | assert mNativeContentViewRenderView != 0; |
66 | nativeSurfaceDestroyed(mNativeContentViewRenderView); |
67 | } |
68 | }; |
69 | mSurfaceView.getHolder().addCallback(mSurfaceCallback); |
70 | |
71 | mVSyncAdapter = new VSyncAdapter(getContext()); |
72 | addView(mSurfaceView, |
73 | new FrameLayout.LayoutParams( |
74 | FrameLayout.LayoutParams.MATCH_PARENT, |
75 | FrameLayout.LayoutParams.MATCH_PARENT)); |
76 | } |
77 | |
78 | private static class VSyncAdapter implements VSyncManager.Provider, VSyncMonitor.Listener { |
79 | private final VSyncMonitor mVSyncMonitor; |
80 | private boolean mVSyncNotificationEnabled; |
81 | private VSyncManager.Listener mVSyncListener; |
82 | |
83 | // The VSyncMonitor gives the timebase for the actual vsync, but we don't want render until |
84 | // we have had a chance for input events to propagate to the compositor thread. This takes |
85 | // 3 ms typically, so we adjust the vsync timestamps forward by a bit to give input events a |
86 | // chance to arrive. |
87 | private static final long INPUT_EVENT_LAG_FROM_VSYNC_MICROSECONDS = 3200; |
88 | |
89 | VSyncAdapter(Context context) { |
90 | mVSyncMonitor = new VSyncMonitor(context, this); |
91 | } |
92 | |
93 | @Override |
94 | public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros) { |
95 | if (mVSyncListener == null) return; |
96 | if (mVSyncNotificationEnabled) { |
97 | mVSyncListener.onVSync(vsyncTimeMicros); |
98 | mVSyncMonitor.requestUpdate(); |
99 | } else { |
100 | // Compensate for input event lag. Input events are delivered immediately on |
101 | // pre-JB releases, so this adjustment is only done for later versions. |
102 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { |
103 | vsyncTimeMicros += INPUT_EVENT_LAG_FROM_VSYNC_MICROSECONDS; |
104 | } |
105 | mVSyncListener.updateVSync(vsyncTimeMicros, |
106 | mVSyncMonitor.getVSyncPeriodInMicroseconds()); |
107 | } |
108 | } |
109 | |
110 | @Override |
111 | public void registerVSyncListener(VSyncManager.Listener listener) { |
112 | if (!mVSyncNotificationEnabled) mVSyncMonitor.requestUpdate(); |
113 | mVSyncNotificationEnabled = true; |
114 | } |
115 | |
116 | @Override |
117 | public void unregisterVSyncListener(VSyncManager.Listener listener) { |
118 | mVSyncNotificationEnabled = false; |
119 | } |
120 | |
121 | void setVSyncListener(VSyncManager.Listener listener) { |
122 | mVSyncListener = listener; |
123 | if (mVSyncListener != null) mVSyncMonitor.requestUpdate(); |
124 | } |
125 | } |
126 | |
127 | /** |
128 | * Should be called when the ContentViewRenderView is not needed anymore so its associated |
129 | * native resource can be freed. |
130 | */ |
131 | public void destroy() { |
132 | mSurfaceView.getHolder().removeCallback(mSurfaceCallback); |
133 | nativeDestroy(mNativeContentViewRenderView); |
134 | mNativeContentViewRenderView = 0; |
135 | } |
136 | |
137 | /** |
138 | * Makes the passed ContentView the one displayed by this ContentViewRenderView. |
139 | */ |
140 | public void setCurrentContentView(ContentView contentView) { |
141 | assert mNativeContentViewRenderView != 0; |
142 | ContentViewCore contentViewCore = contentView.getContentViewCore(); |
143 | nativeSetCurrentContentView(mNativeContentViewRenderView, |
144 | contentViewCore.getNativeContentViewCore()); |
145 | |
146 | mCurrentContentView = contentView; |
147 | contentViewCore.onPhysicalBackingSizeChanged(getWidth(), getHeight()); |
148 | mVSyncAdapter.setVSyncListener(contentViewCore.getVSyncListener(mVSyncAdapter)); |
149 | } |
150 | |
151 | /** |
152 | * This method should be subclassed to provide actions to be performed once the view is ready to |
153 | * render. |
154 | */ |
155 | protected void onReadyToRender() { |
156 | } |
157 | |
158 | /** |
159 | * This method could be subclassed optionally to provide a custom SurfaceView object to |
160 | * this ContentViewRenderView. |
161 | * @param context The context used to create the SurfaceView object. |
162 | * @return The created SurfaceView object. |
163 | */ |
164 | protected SurfaceView createSurfaceView(Context context) { |
165 | return new SurfaceView(context); |
166 | } |
167 | |
168 | /** |
169 | * @return whether the surface view is initialized and ready to render. |
170 | */ |
171 | public boolean isInitialized() { |
172 | return mSurfaceView.getHolder().getSurface() != null; |
173 | } |
174 | |
175 | private static native int nativeInit(); |
176 | private native void nativeDestroy(int nativeContentViewRenderView); |
177 | private native void nativeSetCurrentContentView(int nativeContentViewRenderView, |
178 | int nativeContentView); |
179 | private native void nativeSurfaceCreated(int nativeContentViewRenderView, Surface surface); |
180 | private native void nativeSurfaceDestroyed(int nativeContentViewRenderView); |
181 | private native void nativeSurfaceSetSize(int nativeContentViewRenderView, |
182 | int width, int height); |
183 | } |