|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.gfx; |
|
7 |
|
8 import org.mozilla.gecko.GeckoAccessibility; |
|
9 import org.mozilla.gecko.GeckoAppShell; |
|
10 import org.mozilla.gecko.GeckoEvent; |
|
11 import org.mozilla.gecko.PrefsHelper; |
|
12 import org.mozilla.gecko.R; |
|
13 import org.mozilla.gecko.Tab; |
|
14 import org.mozilla.gecko.Tabs; |
|
15 import org.mozilla.gecko.TouchEventInterceptor; |
|
16 import org.mozilla.gecko.ZoomConstraints; |
|
17 import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI; |
|
18 import org.mozilla.gecko.mozglue.RobocopTarget; |
|
19 import org.mozilla.gecko.EventDispatcher; |
|
20 |
|
21 import android.content.Context; |
|
22 import android.graphics.Bitmap; |
|
23 import android.graphics.BitmapFactory; |
|
24 import android.graphics.Canvas; |
|
25 import android.graphics.Color; |
|
26 import android.graphics.Point; |
|
27 import android.graphics.PointF; |
|
28 import android.graphics.Rect; |
|
29 import android.graphics.SurfaceTexture; |
|
30 import android.os.Build; |
|
31 import android.os.Handler; |
|
32 import android.util.AttributeSet; |
|
33 import android.util.DisplayMetrics; |
|
34 import android.util.Log; |
|
35 import android.view.KeyEvent; |
|
36 import android.view.MotionEvent; |
|
37 import android.view.SurfaceHolder; |
|
38 import android.view.SurfaceView; |
|
39 import android.view.TextureView; |
|
40 import android.view.View; |
|
41 import android.view.ViewGroup; |
|
42 import android.view.inputmethod.EditorInfo; |
|
43 import android.view.inputmethod.InputConnection; |
|
44 import android.widget.FrameLayout; |
|
45 |
|
46 import java.nio.IntBuffer; |
|
47 import java.util.ArrayList; |
|
48 |
|
49 /** |
|
50 * A view rendered by the layer compositor. |
|
51 * |
|
52 * Note that LayerView is accessed by Robocop via reflection. |
|
53 */ |
|
54 public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener { |
|
55 private static String LOGTAG = "GeckoLayerView"; |
|
56 |
|
57 private GeckoLayerClient mLayerClient; |
|
58 private PanZoomController mPanZoomController; |
|
59 private LayerMarginsAnimator mMarginsAnimator; |
|
60 private GLController mGLController; |
|
61 private InputConnectionHandler mInputConnectionHandler; |
|
62 private LayerRenderer mRenderer; |
|
63 /* Must be a PAINT_xxx constant */ |
|
64 private int mPaintState; |
|
65 private int mBackgroundColor; |
|
66 private boolean mFullScreen; |
|
67 |
|
68 private SurfaceView mSurfaceView; |
|
69 private TextureView mTextureView; |
|
70 |
|
71 private Listener mListener; |
|
72 |
|
73 /* This should only be modified on the Java UI thread. */ |
|
74 private final ArrayList<TouchEventInterceptor> mTouchInterceptors; |
|
75 private final Overscroll mOverscroll; |
|
76 |
|
77 /* Flags used to determine when to show the painted surface. */ |
|
78 public static final int PAINT_START = 0; |
|
79 public static final int PAINT_BEFORE_FIRST = 1; |
|
80 public static final int PAINT_AFTER_FIRST = 2; |
|
81 |
|
82 public boolean shouldUseTextureView() { |
|
83 // Disable TextureView support for now as it causes panning/zooming |
|
84 // performance regressions (see bug 792259). Uncomment the code below |
|
85 // once this bug is fixed. |
|
86 return false; |
|
87 |
|
88 /* |
|
89 // we can only use TextureView on ICS or higher |
|
90 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { |
|
91 Log.i(LOGTAG, "Not using TextureView: not on ICS+"); |
|
92 return false; |
|
93 } |
|
94 |
|
95 try { |
|
96 // and then we can only use it if we have a hardware accelerated window |
|
97 Method m = View.class.getMethod("isHardwareAccelerated", (Class[]) null); |
|
98 return (Boolean) m.invoke(this); |
|
99 } catch (Exception e) { |
|
100 Log.i(LOGTAG, "Not using TextureView: caught exception checking for hw accel: " + e.toString()); |
|
101 return false; |
|
102 } */ |
|
103 } |
|
104 |
|
105 public LayerView(Context context, AttributeSet attrs) { |
|
106 super(context, attrs); |
|
107 |
|
108 mGLController = GLController.getInstance(this); |
|
109 mPaintState = PAINT_START; |
|
110 mBackgroundColor = Color.WHITE; |
|
111 |
|
112 mTouchInterceptors = new ArrayList<TouchEventInterceptor>(); |
|
113 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { |
|
114 mOverscroll = new OverscrollEdgeEffect(this); |
|
115 } else { |
|
116 mOverscroll = null; |
|
117 } |
|
118 Tabs.registerOnTabsChangedListener(this); |
|
119 } |
|
120 |
|
121 public LayerView(Context context) { |
|
122 this(context, null); |
|
123 } |
|
124 |
|
125 public void initializeView(EventDispatcher eventDispatcher) { |
|
126 mLayerClient = new GeckoLayerClient(getContext(), this, eventDispatcher); |
|
127 if (mOverscroll != null) { |
|
128 mLayerClient.setOverscrollHandler(mOverscroll); |
|
129 } |
|
130 |
|
131 mPanZoomController = mLayerClient.getPanZoomController(); |
|
132 mMarginsAnimator = mLayerClient.getLayerMarginsAnimator(); |
|
133 |
|
134 mRenderer = new LayerRenderer(this); |
|
135 mInputConnectionHandler = null; |
|
136 |
|
137 setFocusable(true); |
|
138 setFocusableInTouchMode(true); |
|
139 |
|
140 GeckoAccessibility.setDelegate(this); |
|
141 } |
|
142 |
|
143 private Point getEventRadius(MotionEvent event) { |
|
144 if (Build.VERSION.SDK_INT >= 9) { |
|
145 return new Point((int)event.getToolMajor()/2, |
|
146 (int)event.getToolMinor()/2); |
|
147 } |
|
148 |
|
149 float size = event.getSize(); |
|
150 DisplayMetrics displaymetrics = getContext().getResources().getDisplayMetrics(); |
|
151 size = size * Math.min(displaymetrics.heightPixels, displaymetrics.widthPixels); |
|
152 return new Point((int)size, (int)size); |
|
153 } |
|
154 |
|
155 public void geckoConnected() { |
|
156 // See if we want to force 16-bit colour before doing anything |
|
157 PrefsHelper.getPref("gfx.android.rgb16.force", new PrefsHelper.PrefHandlerBase() { |
|
158 @Override public void prefValue(String pref, boolean force16bit) { |
|
159 if (force16bit) { |
|
160 GeckoAppShell.setScreenDepthOverride(16); |
|
161 } |
|
162 } |
|
163 }); |
|
164 |
|
165 mLayerClient.notifyGeckoReady(); |
|
166 addTouchInterceptor(new TouchEventInterceptor() { |
|
167 private PointF mInitialTouchPoint = null; |
|
168 |
|
169 @Override |
|
170 public boolean onInterceptTouchEvent(View view, MotionEvent event) { |
|
171 return false; |
|
172 } |
|
173 |
|
174 @Override |
|
175 public boolean onTouch(View view, MotionEvent event) { |
|
176 if (event == null) { |
|
177 return true; |
|
178 } |
|
179 |
|
180 int action = event.getActionMasked(); |
|
181 PointF point = new PointF(event.getX(), event.getY()); |
|
182 if (action == MotionEvent.ACTION_DOWN) { |
|
183 mInitialTouchPoint = point; |
|
184 } |
|
185 |
|
186 if (mInitialTouchPoint != null && action == MotionEvent.ACTION_MOVE) { |
|
187 Point p = getEventRadius(event); |
|
188 |
|
189 if (PointUtils.subtract(point, mInitialTouchPoint).length() < |
|
190 Math.max(PanZoomController.CLICK_THRESHOLD, Math.min(Math.min(p.x, p.y), PanZoomController.PAN_THRESHOLD))) { |
|
191 // Don't send the touchmove event if if the users finger hasn't moved far. |
|
192 // Necessary for Google Maps to work correctly. See bug 771099. |
|
193 return true; |
|
194 } else { |
|
195 mInitialTouchPoint = null; |
|
196 } |
|
197 } |
|
198 |
|
199 GeckoAppShell.sendEventToGecko(GeckoEvent.createMotionEvent(event, false)); |
|
200 return true; |
|
201 } |
|
202 }); |
|
203 } |
|
204 |
|
205 public void showSurface() { |
|
206 // Fix this if TextureView support is turned back on above |
|
207 mSurfaceView.setVisibility(View.VISIBLE); |
|
208 } |
|
209 |
|
210 public void hideSurface() { |
|
211 // Fix this if TextureView support is turned back on above |
|
212 mSurfaceView.setVisibility(View.INVISIBLE); |
|
213 } |
|
214 |
|
215 public void destroy() { |
|
216 if (mLayerClient != null) { |
|
217 mLayerClient.destroy(); |
|
218 } |
|
219 if (mRenderer != null) { |
|
220 mRenderer.destroy(); |
|
221 } |
|
222 Tabs.unregisterOnTabsChangedListener(this); |
|
223 } |
|
224 |
|
225 public void addTouchInterceptor(final TouchEventInterceptor aTouchInterceptor) { |
|
226 post(new Runnable() { |
|
227 @Override |
|
228 public void run() { |
|
229 mTouchInterceptors.add(aTouchInterceptor); |
|
230 } |
|
231 }); |
|
232 } |
|
233 |
|
234 public void removeTouchInterceptor(final TouchEventInterceptor aTouchInterceptor) { |
|
235 post(new Runnable() { |
|
236 @Override |
|
237 public void run() { |
|
238 mTouchInterceptors.remove(aTouchInterceptor); |
|
239 } |
|
240 }); |
|
241 } |
|
242 |
|
243 private boolean runTouchInterceptors(MotionEvent event, boolean aOnTouch) { |
|
244 boolean result = false; |
|
245 for (TouchEventInterceptor i : mTouchInterceptors) { |
|
246 if (aOnTouch) { |
|
247 result |= i.onTouch(this, event); |
|
248 } else { |
|
249 result |= i.onInterceptTouchEvent(this, event); |
|
250 } |
|
251 } |
|
252 |
|
253 return result; |
|
254 } |
|
255 |
|
256 @Override |
|
257 public void dispatchDraw(final Canvas canvas) { |
|
258 super.dispatchDraw(canvas); |
|
259 |
|
260 // We must have a layer client to get valid viewport metrics |
|
261 if (mLayerClient != null && mOverscroll != null) { |
|
262 mOverscroll.draw(canvas, getViewportMetrics()); |
|
263 } |
|
264 } |
|
265 |
|
266 @Override |
|
267 public boolean onTouchEvent(MotionEvent event) { |
|
268 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { |
|
269 requestFocus(); |
|
270 } |
|
271 |
|
272 if (runTouchInterceptors(event, false)) { |
|
273 return true; |
|
274 } |
|
275 if (mPanZoomController != null && mPanZoomController.onTouchEvent(event)) { |
|
276 return true; |
|
277 } |
|
278 if (runTouchInterceptors(event, true)) { |
|
279 return true; |
|
280 } |
|
281 return false; |
|
282 } |
|
283 |
|
284 @Override |
|
285 public boolean onHoverEvent(MotionEvent event) { |
|
286 if (runTouchInterceptors(event, true)) { |
|
287 return true; |
|
288 } |
|
289 return false; |
|
290 } |
|
291 |
|
292 @Override |
|
293 public boolean onGenericMotionEvent(MotionEvent event) { |
|
294 if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) { |
|
295 return true; |
|
296 } |
|
297 return false; |
|
298 } |
|
299 |
|
300 @Override |
|
301 protected void onAttachedToWindow() { |
|
302 // This check should not be done before the view is attached to a window |
|
303 // as hardware acceleration will not be enabled at that point. |
|
304 // We must create and add the SurfaceView instance before the view tree |
|
305 // is fully created to avoid flickering (see bug 801477). |
|
306 if (shouldUseTextureView()) { |
|
307 mTextureView = new TextureView(getContext()); |
|
308 mTextureView.setSurfaceTextureListener(new SurfaceTextureListener()); |
|
309 |
|
310 // The background is set to this color when the LayerView is |
|
311 // created, and it will be shown immediately at startup. Shortly |
|
312 // after, the tab's background color will be used before any content |
|
313 // is shown. |
|
314 mTextureView.setBackgroundColor(Color.WHITE); |
|
315 addView(mTextureView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); |
|
316 } else { |
|
317 // This will stop PropertyAnimator from creating a drawing cache (i.e. a bitmap) |
|
318 // from a SurfaceView, which is just not possible (the bitmap will be transparent). |
|
319 setWillNotCacheDrawing(false); |
|
320 |
|
321 mSurfaceView = new LayerSurfaceView(getContext(), this); |
|
322 mSurfaceView.setBackgroundColor(Color.WHITE); |
|
323 addView(mSurfaceView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); |
|
324 |
|
325 SurfaceHolder holder = mSurfaceView.getHolder(); |
|
326 holder.addCallback(new SurfaceListener()); |
|
327 } |
|
328 } |
|
329 |
|
330 // Don't expose GeckoLayerClient to things outside this package; only expose it as an Object |
|
331 GeckoLayerClient getLayerClient() { return mLayerClient; } |
|
332 public Object getLayerClientObject() { return mLayerClient; } |
|
333 |
|
334 public PanZoomController getPanZoomController() { return mPanZoomController; } |
|
335 public LayerMarginsAnimator getLayerMarginsAnimator() { return mMarginsAnimator; } |
|
336 |
|
337 public ImmutableViewportMetrics getViewportMetrics() { |
|
338 return mLayerClient.getViewportMetrics(); |
|
339 } |
|
340 |
|
341 public void abortPanning() { |
|
342 if (mPanZoomController != null) { |
|
343 mPanZoomController.abortPanning(); |
|
344 } |
|
345 } |
|
346 |
|
347 public PointF convertViewPointToLayerPoint(PointF viewPoint) { |
|
348 return mLayerClient.convertViewPointToLayerPoint(viewPoint); |
|
349 } |
|
350 |
|
351 int getBackgroundColor() { |
|
352 return mBackgroundColor; |
|
353 } |
|
354 |
|
355 @Override |
|
356 public void setBackgroundColor(int newColor) { |
|
357 mBackgroundColor = newColor; |
|
358 requestRender(); |
|
359 } |
|
360 |
|
361 public void setZoomConstraints(ZoomConstraints constraints) { |
|
362 mLayerClient.setZoomConstraints(constraints); |
|
363 } |
|
364 |
|
365 public void setIsRTL(boolean aIsRTL) { |
|
366 mLayerClient.setIsRTL(aIsRTL); |
|
367 } |
|
368 |
|
369 public void setInputConnectionHandler(InputConnectionHandler inputConnectionHandler) { |
|
370 mInputConnectionHandler = inputConnectionHandler; |
|
371 mLayerClient.forceRedraw(null); |
|
372 } |
|
373 |
|
374 @Override |
|
375 public Handler getHandler() { |
|
376 if (mInputConnectionHandler != null) |
|
377 return mInputConnectionHandler.getHandler(super.getHandler()); |
|
378 return super.getHandler(); |
|
379 } |
|
380 |
|
381 @Override |
|
382 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { |
|
383 if (mInputConnectionHandler != null) |
|
384 return mInputConnectionHandler.onCreateInputConnection(outAttrs); |
|
385 return null; |
|
386 } |
|
387 |
|
388 @Override |
|
389 public boolean onKeyPreIme(int keyCode, KeyEvent event) { |
|
390 if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyPreIme(keyCode, event)) { |
|
391 return true; |
|
392 } |
|
393 return false; |
|
394 } |
|
395 |
|
396 @Override |
|
397 public boolean onKeyDown(int keyCode, KeyEvent event) { |
|
398 if (mPanZoomController != null && mPanZoomController.onKeyEvent(event)) { |
|
399 return true; |
|
400 } |
|
401 if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyDown(keyCode, event)) { |
|
402 return true; |
|
403 } |
|
404 return false; |
|
405 } |
|
406 |
|
407 @Override |
|
408 public boolean onKeyLongPress(int keyCode, KeyEvent event) { |
|
409 if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyLongPress(keyCode, event)) { |
|
410 return true; |
|
411 } |
|
412 return false; |
|
413 } |
|
414 |
|
415 @Override |
|
416 public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { |
|
417 if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyMultiple(keyCode, repeatCount, event)) { |
|
418 return true; |
|
419 } |
|
420 return false; |
|
421 } |
|
422 |
|
423 @Override |
|
424 public boolean onKeyUp(int keyCode, KeyEvent event) { |
|
425 if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyUp(keyCode, event)) { |
|
426 return true; |
|
427 } |
|
428 return false; |
|
429 } |
|
430 |
|
431 public boolean isIMEEnabled() { |
|
432 if (mInputConnectionHandler != null) { |
|
433 return mInputConnectionHandler.isIMEEnabled(); |
|
434 } |
|
435 return false; |
|
436 } |
|
437 |
|
438 public void requestRender() { |
|
439 if (mListener != null) { |
|
440 mListener.renderRequested(); |
|
441 } |
|
442 } |
|
443 |
|
444 public void addLayer(Layer layer) { |
|
445 mRenderer.addLayer(layer); |
|
446 } |
|
447 |
|
448 public void removeLayer(Layer layer) { |
|
449 mRenderer.removeLayer(layer); |
|
450 } |
|
451 |
|
452 public void postRenderTask(RenderTask task) { |
|
453 mRenderer.postRenderTask(task); |
|
454 } |
|
455 |
|
456 public void removeRenderTask(RenderTask task) { |
|
457 mRenderer.removeRenderTask(task); |
|
458 } |
|
459 |
|
460 public int getMaxTextureSize() { |
|
461 return mRenderer.getMaxTextureSize(); |
|
462 } |
|
463 |
|
464 /** Used by robocop for testing purposes. Not for production use! */ |
|
465 @RobocopTarget |
|
466 public IntBuffer getPixels() { |
|
467 return mRenderer.getPixels(); |
|
468 } |
|
469 |
|
470 /* paintState must be a PAINT_xxx constant. */ |
|
471 public void setPaintState(int paintState) { |
|
472 mPaintState = paintState; |
|
473 } |
|
474 |
|
475 public int getPaintState() { |
|
476 return mPaintState; |
|
477 } |
|
478 |
|
479 public LayerRenderer getRenderer() { |
|
480 return mRenderer; |
|
481 } |
|
482 |
|
483 public void setListener(Listener listener) { |
|
484 mListener = listener; |
|
485 } |
|
486 |
|
487 Listener getListener() { |
|
488 return mListener; |
|
489 } |
|
490 |
|
491 public GLController getGLController() { |
|
492 return mGLController; |
|
493 } |
|
494 |
|
495 private Bitmap getDrawable(String name) { |
|
496 BitmapFactory.Options options = new BitmapFactory.Options(); |
|
497 options.inScaled = false; |
|
498 Context context = getContext(); |
|
499 int resId = context.getResources().getIdentifier(name, "drawable", context.getPackageName()); |
|
500 return BitmapUtils.decodeResource(context, resId, options); |
|
501 } |
|
502 |
|
503 Bitmap getScrollbarImage() { |
|
504 return getDrawable("scrollbar"); |
|
505 } |
|
506 |
|
507 /* When using a SurfaceView (mSurfaceView != null), resizing happens in two |
|
508 * phases. First, the LayerView changes size, then, often some frames later, |
|
509 * the SurfaceView changes size. Because of this, we need to split the |
|
510 * resize into two phases to avoid jittering. |
|
511 * |
|
512 * The first phase is the LayerView size change. mListener is notified so |
|
513 * that a synchronous draw can be performed (otherwise a blank frame will |
|
514 * appear). |
|
515 * |
|
516 * The second phase is the SurfaceView size change. At this point, the |
|
517 * backing GL surface is resized and another synchronous draw is performed. |
|
518 * Gecko is also sent the new window size, and this will likely cause an |
|
519 * extra draw a few frames later, after it's re-rendered and caught up. |
|
520 * |
|
521 * In the case that there is no valid GL surface (for example, when |
|
522 * resuming, or when coming back from the awesomescreen), or we're using a |
|
523 * TextureView instead of a SurfaceView, the first phase is skipped. |
|
524 */ |
|
525 private void onSizeChanged(int width, int height) { |
|
526 if (!mGLController.isCompositorCreated()) { |
|
527 return; |
|
528 } |
|
529 |
|
530 surfaceChanged(width, height); |
|
531 |
|
532 if (mSurfaceView == null) { |
|
533 return; |
|
534 } |
|
535 |
|
536 if (mListener != null) { |
|
537 mListener.sizeChanged(width, height); |
|
538 } |
|
539 |
|
540 if (mOverscroll != null) { |
|
541 mOverscroll.setSize(width, height); |
|
542 } |
|
543 } |
|
544 |
|
545 private void surfaceChanged(int width, int height) { |
|
546 mGLController.serverSurfaceChanged(width, height); |
|
547 |
|
548 if (mListener != null) { |
|
549 mListener.surfaceChanged(width, height); |
|
550 } |
|
551 |
|
552 if (mOverscroll != null) { |
|
553 mOverscroll.setSize(width, height); |
|
554 } |
|
555 } |
|
556 |
|
557 private void onDestroyed() { |
|
558 mGLController.serverSurfaceDestroyed(); |
|
559 } |
|
560 |
|
561 public Object getNativeWindow() { |
|
562 if (mSurfaceView != null) |
|
563 return mSurfaceView.getHolder(); |
|
564 |
|
565 return mTextureView.getSurfaceTexture(); |
|
566 } |
|
567 |
|
568 @WrapElementForJNI(allowMultithread = true, stubName = "RegisterCompositorWrapper") |
|
569 public static GLController registerCxxCompositor() { |
|
570 try { |
|
571 LayerView layerView = GeckoAppShell.getLayerView(); |
|
572 GLController controller = layerView.getGLController(); |
|
573 controller.compositorCreated(); |
|
574 return controller; |
|
575 } catch (Exception e) { |
|
576 Log.e(LOGTAG, "Error registering compositor!", e); |
|
577 return null; |
|
578 } |
|
579 } |
|
580 |
|
581 public interface Listener { |
|
582 void renderRequested(); |
|
583 void sizeChanged(int width, int height); |
|
584 void surfaceChanged(int width, int height); |
|
585 } |
|
586 |
|
587 private class SurfaceListener implements SurfaceHolder.Callback { |
|
588 @Override |
|
589 public void surfaceChanged(SurfaceHolder holder, int format, int width, |
|
590 int height) { |
|
591 onSizeChanged(width, height); |
|
592 } |
|
593 |
|
594 @Override |
|
595 public void surfaceCreated(SurfaceHolder holder) { |
|
596 } |
|
597 |
|
598 @Override |
|
599 public void surfaceDestroyed(SurfaceHolder holder) { |
|
600 onDestroyed(); |
|
601 } |
|
602 } |
|
603 |
|
604 /* A subclass of SurfaceView to listen to layout changes, as |
|
605 * View.OnLayoutChangeListener requires API level 11. |
|
606 */ |
|
607 private class LayerSurfaceView extends SurfaceView { |
|
608 LayerView mParent; |
|
609 |
|
610 public LayerSurfaceView(Context aContext, LayerView aParent) { |
|
611 super(aContext); |
|
612 mParent = aParent; |
|
613 } |
|
614 |
|
615 @Override |
|
616 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
|
617 if (changed) { |
|
618 mParent.surfaceChanged(right - left, bottom - top); |
|
619 } |
|
620 } |
|
621 } |
|
622 |
|
623 private class SurfaceTextureListener implements TextureView.SurfaceTextureListener { |
|
624 @Override |
|
625 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { |
|
626 // We don't do this for surfaceCreated above because it is always followed by a surfaceChanged, |
|
627 // but that is not the case here. |
|
628 onSizeChanged(width, height); |
|
629 } |
|
630 |
|
631 @Override |
|
632 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { |
|
633 onDestroyed(); |
|
634 return true; // allow Android to call release() on the SurfaceTexture, we are done drawing to it |
|
635 } |
|
636 |
|
637 @Override |
|
638 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { |
|
639 onSizeChanged(width, height); |
|
640 } |
|
641 |
|
642 @Override |
|
643 public void onSurfaceTextureUpdated(SurfaceTexture surface) { |
|
644 |
|
645 } |
|
646 } |
|
647 |
|
648 @RobocopTarget |
|
649 public void addDrawListener(DrawListener listener) { |
|
650 mLayerClient.addDrawListener(listener); |
|
651 } |
|
652 |
|
653 @RobocopTarget |
|
654 public void removeDrawListener(DrawListener listener) { |
|
655 mLayerClient.removeDrawListener(listener); |
|
656 } |
|
657 |
|
658 @RobocopTarget |
|
659 public static interface DrawListener { |
|
660 public void drawFinished(); |
|
661 } |
|
662 |
|
663 @Override |
|
664 public void setOverScrollMode(int overscrollMode) { |
|
665 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { |
|
666 super.setOverScrollMode(overscrollMode); |
|
667 } |
|
668 if (mPanZoomController != null) { |
|
669 mPanZoomController.setOverScrollMode(overscrollMode); |
|
670 } |
|
671 } |
|
672 |
|
673 @Override |
|
674 public int getOverScrollMode() { |
|
675 if (mPanZoomController != null) { |
|
676 return mPanZoomController.getOverScrollMode(); |
|
677 } |
|
678 |
|
679 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { |
|
680 return super.getOverScrollMode(); |
|
681 } |
|
682 return View.OVER_SCROLL_ALWAYS; |
|
683 } |
|
684 |
|
685 @Override |
|
686 public void onFocusChanged (boolean gainFocus, int direction, Rect previouslyFocusedRect) { |
|
687 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); |
|
688 GeckoAccessibility.onLayerViewFocusChanged(this, gainFocus); |
|
689 } |
|
690 |
|
691 public void setFullScreen(boolean fullScreen) { |
|
692 mFullScreen = fullScreen; |
|
693 } |
|
694 |
|
695 public boolean isFullScreen() { |
|
696 return mFullScreen; |
|
697 } |
|
698 |
|
699 @Override |
|
700 public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { |
|
701 if (msg == Tabs.TabEvents.VIEWPORT_CHANGE && Tabs.getInstance().isSelectedTab(tab) && mLayerClient != null) { |
|
702 setZoomConstraints(tab.getZoomConstraints()); |
|
703 setIsRTL(tab.getIsRTL()); |
|
704 } |
|
705 } |
|
706 |
|
707 // Public hooks for listening to metrics changing |
|
708 |
|
709 public interface OnMetricsChangedListener { |
|
710 public void onMetricsChanged(ImmutableViewportMetrics viewport); |
|
711 public void onPanZoomStopped(); |
|
712 } |
|
713 |
|
714 public void setOnMetricsChangedListener(OnMetricsChangedListener listener) { |
|
715 mLayerClient.setOnMetricsChangedListener(listener); |
|
716 } |
|
717 } |