1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/gfx/LayerView.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,717 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.gfx; 1.10 + 1.11 +import org.mozilla.gecko.GeckoAccessibility; 1.12 +import org.mozilla.gecko.GeckoAppShell; 1.13 +import org.mozilla.gecko.GeckoEvent; 1.14 +import org.mozilla.gecko.PrefsHelper; 1.15 +import org.mozilla.gecko.R; 1.16 +import org.mozilla.gecko.Tab; 1.17 +import org.mozilla.gecko.Tabs; 1.18 +import org.mozilla.gecko.TouchEventInterceptor; 1.19 +import org.mozilla.gecko.ZoomConstraints; 1.20 +import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI; 1.21 +import org.mozilla.gecko.mozglue.RobocopTarget; 1.22 +import org.mozilla.gecko.EventDispatcher; 1.23 + 1.24 +import android.content.Context; 1.25 +import android.graphics.Bitmap; 1.26 +import android.graphics.BitmapFactory; 1.27 +import android.graphics.Canvas; 1.28 +import android.graphics.Color; 1.29 +import android.graphics.Point; 1.30 +import android.graphics.PointF; 1.31 +import android.graphics.Rect; 1.32 +import android.graphics.SurfaceTexture; 1.33 +import android.os.Build; 1.34 +import android.os.Handler; 1.35 +import android.util.AttributeSet; 1.36 +import android.util.DisplayMetrics; 1.37 +import android.util.Log; 1.38 +import android.view.KeyEvent; 1.39 +import android.view.MotionEvent; 1.40 +import android.view.SurfaceHolder; 1.41 +import android.view.SurfaceView; 1.42 +import android.view.TextureView; 1.43 +import android.view.View; 1.44 +import android.view.ViewGroup; 1.45 +import android.view.inputmethod.EditorInfo; 1.46 +import android.view.inputmethod.InputConnection; 1.47 +import android.widget.FrameLayout; 1.48 + 1.49 +import java.nio.IntBuffer; 1.50 +import java.util.ArrayList; 1.51 + 1.52 +/** 1.53 + * A view rendered by the layer compositor. 1.54 + * 1.55 + * Note that LayerView is accessed by Robocop via reflection. 1.56 + */ 1.57 +public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener { 1.58 + private static String LOGTAG = "GeckoLayerView"; 1.59 + 1.60 + private GeckoLayerClient mLayerClient; 1.61 + private PanZoomController mPanZoomController; 1.62 + private LayerMarginsAnimator mMarginsAnimator; 1.63 + private GLController mGLController; 1.64 + private InputConnectionHandler mInputConnectionHandler; 1.65 + private LayerRenderer mRenderer; 1.66 + /* Must be a PAINT_xxx constant */ 1.67 + private int mPaintState; 1.68 + private int mBackgroundColor; 1.69 + private boolean mFullScreen; 1.70 + 1.71 + private SurfaceView mSurfaceView; 1.72 + private TextureView mTextureView; 1.73 + 1.74 + private Listener mListener; 1.75 + 1.76 + /* This should only be modified on the Java UI thread. */ 1.77 + private final ArrayList<TouchEventInterceptor> mTouchInterceptors; 1.78 + private final Overscroll mOverscroll; 1.79 + 1.80 + /* Flags used to determine when to show the painted surface. */ 1.81 + public static final int PAINT_START = 0; 1.82 + public static final int PAINT_BEFORE_FIRST = 1; 1.83 + public static final int PAINT_AFTER_FIRST = 2; 1.84 + 1.85 + public boolean shouldUseTextureView() { 1.86 + // Disable TextureView support for now as it causes panning/zooming 1.87 + // performance regressions (see bug 792259). Uncomment the code below 1.88 + // once this bug is fixed. 1.89 + return false; 1.90 + 1.91 + /* 1.92 + // we can only use TextureView on ICS or higher 1.93 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 1.94 + Log.i(LOGTAG, "Not using TextureView: not on ICS+"); 1.95 + return false; 1.96 + } 1.97 + 1.98 + try { 1.99 + // and then we can only use it if we have a hardware accelerated window 1.100 + Method m = View.class.getMethod("isHardwareAccelerated", (Class[]) null); 1.101 + return (Boolean) m.invoke(this); 1.102 + } catch (Exception e) { 1.103 + Log.i(LOGTAG, "Not using TextureView: caught exception checking for hw accel: " + e.toString()); 1.104 + return false; 1.105 + } */ 1.106 + } 1.107 + 1.108 + public LayerView(Context context, AttributeSet attrs) { 1.109 + super(context, attrs); 1.110 + 1.111 + mGLController = GLController.getInstance(this); 1.112 + mPaintState = PAINT_START; 1.113 + mBackgroundColor = Color.WHITE; 1.114 + 1.115 + mTouchInterceptors = new ArrayList<TouchEventInterceptor>(); 1.116 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 1.117 + mOverscroll = new OverscrollEdgeEffect(this); 1.118 + } else { 1.119 + mOverscroll = null; 1.120 + } 1.121 + Tabs.registerOnTabsChangedListener(this); 1.122 + } 1.123 + 1.124 + public LayerView(Context context) { 1.125 + this(context, null); 1.126 + } 1.127 + 1.128 + public void initializeView(EventDispatcher eventDispatcher) { 1.129 + mLayerClient = new GeckoLayerClient(getContext(), this, eventDispatcher); 1.130 + if (mOverscroll != null) { 1.131 + mLayerClient.setOverscrollHandler(mOverscroll); 1.132 + } 1.133 + 1.134 + mPanZoomController = mLayerClient.getPanZoomController(); 1.135 + mMarginsAnimator = mLayerClient.getLayerMarginsAnimator(); 1.136 + 1.137 + mRenderer = new LayerRenderer(this); 1.138 + mInputConnectionHandler = null; 1.139 + 1.140 + setFocusable(true); 1.141 + setFocusableInTouchMode(true); 1.142 + 1.143 + GeckoAccessibility.setDelegate(this); 1.144 + } 1.145 + 1.146 + private Point getEventRadius(MotionEvent event) { 1.147 + if (Build.VERSION.SDK_INT >= 9) { 1.148 + return new Point((int)event.getToolMajor()/2, 1.149 + (int)event.getToolMinor()/2); 1.150 + } 1.151 + 1.152 + float size = event.getSize(); 1.153 + DisplayMetrics displaymetrics = getContext().getResources().getDisplayMetrics(); 1.154 + size = size * Math.min(displaymetrics.heightPixels, displaymetrics.widthPixels); 1.155 + return new Point((int)size, (int)size); 1.156 + } 1.157 + 1.158 + public void geckoConnected() { 1.159 + // See if we want to force 16-bit colour before doing anything 1.160 + PrefsHelper.getPref("gfx.android.rgb16.force", new PrefsHelper.PrefHandlerBase() { 1.161 + @Override public void prefValue(String pref, boolean force16bit) { 1.162 + if (force16bit) { 1.163 + GeckoAppShell.setScreenDepthOverride(16); 1.164 + } 1.165 + } 1.166 + }); 1.167 + 1.168 + mLayerClient.notifyGeckoReady(); 1.169 + addTouchInterceptor(new TouchEventInterceptor() { 1.170 + private PointF mInitialTouchPoint = null; 1.171 + 1.172 + @Override 1.173 + public boolean onInterceptTouchEvent(View view, MotionEvent event) { 1.174 + return false; 1.175 + } 1.176 + 1.177 + @Override 1.178 + public boolean onTouch(View view, MotionEvent event) { 1.179 + if (event == null) { 1.180 + return true; 1.181 + } 1.182 + 1.183 + int action = event.getActionMasked(); 1.184 + PointF point = new PointF(event.getX(), event.getY()); 1.185 + if (action == MotionEvent.ACTION_DOWN) { 1.186 + mInitialTouchPoint = point; 1.187 + } 1.188 + 1.189 + if (mInitialTouchPoint != null && action == MotionEvent.ACTION_MOVE) { 1.190 + Point p = getEventRadius(event); 1.191 + 1.192 + if (PointUtils.subtract(point, mInitialTouchPoint).length() < 1.193 + Math.max(PanZoomController.CLICK_THRESHOLD, Math.min(Math.min(p.x, p.y), PanZoomController.PAN_THRESHOLD))) { 1.194 + // Don't send the touchmove event if if the users finger hasn't moved far. 1.195 + // Necessary for Google Maps to work correctly. See bug 771099. 1.196 + return true; 1.197 + } else { 1.198 + mInitialTouchPoint = null; 1.199 + } 1.200 + } 1.201 + 1.202 + GeckoAppShell.sendEventToGecko(GeckoEvent.createMotionEvent(event, false)); 1.203 + return true; 1.204 + } 1.205 + }); 1.206 + } 1.207 + 1.208 + public void showSurface() { 1.209 + // Fix this if TextureView support is turned back on above 1.210 + mSurfaceView.setVisibility(View.VISIBLE); 1.211 + } 1.212 + 1.213 + public void hideSurface() { 1.214 + // Fix this if TextureView support is turned back on above 1.215 + mSurfaceView.setVisibility(View.INVISIBLE); 1.216 + } 1.217 + 1.218 + public void destroy() { 1.219 + if (mLayerClient != null) { 1.220 + mLayerClient.destroy(); 1.221 + } 1.222 + if (mRenderer != null) { 1.223 + mRenderer.destroy(); 1.224 + } 1.225 + Tabs.unregisterOnTabsChangedListener(this); 1.226 + } 1.227 + 1.228 + public void addTouchInterceptor(final TouchEventInterceptor aTouchInterceptor) { 1.229 + post(new Runnable() { 1.230 + @Override 1.231 + public void run() { 1.232 + mTouchInterceptors.add(aTouchInterceptor); 1.233 + } 1.234 + }); 1.235 + } 1.236 + 1.237 + public void removeTouchInterceptor(final TouchEventInterceptor aTouchInterceptor) { 1.238 + post(new Runnable() { 1.239 + @Override 1.240 + public void run() { 1.241 + mTouchInterceptors.remove(aTouchInterceptor); 1.242 + } 1.243 + }); 1.244 + } 1.245 + 1.246 + private boolean runTouchInterceptors(MotionEvent event, boolean aOnTouch) { 1.247 + boolean result = false; 1.248 + for (TouchEventInterceptor i : mTouchInterceptors) { 1.249 + if (aOnTouch) { 1.250 + result |= i.onTouch(this, event); 1.251 + } else { 1.252 + result |= i.onInterceptTouchEvent(this, event); 1.253 + } 1.254 + } 1.255 + 1.256 + return result; 1.257 + } 1.258 + 1.259 + @Override 1.260 + public void dispatchDraw(final Canvas canvas) { 1.261 + super.dispatchDraw(canvas); 1.262 + 1.263 + // We must have a layer client to get valid viewport metrics 1.264 + if (mLayerClient != null && mOverscroll != null) { 1.265 + mOverscroll.draw(canvas, getViewportMetrics()); 1.266 + } 1.267 + } 1.268 + 1.269 + @Override 1.270 + public boolean onTouchEvent(MotionEvent event) { 1.271 + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 1.272 + requestFocus(); 1.273 + } 1.274 + 1.275 + if (runTouchInterceptors(event, false)) { 1.276 + return true; 1.277 + } 1.278 + if (mPanZoomController != null && mPanZoomController.onTouchEvent(event)) { 1.279 + return true; 1.280 + } 1.281 + if (runTouchInterceptors(event, true)) { 1.282 + return true; 1.283 + } 1.284 + return false; 1.285 + } 1.286 + 1.287 + @Override 1.288 + public boolean onHoverEvent(MotionEvent event) { 1.289 + if (runTouchInterceptors(event, true)) { 1.290 + return true; 1.291 + } 1.292 + return false; 1.293 + } 1.294 + 1.295 + @Override 1.296 + public boolean onGenericMotionEvent(MotionEvent event) { 1.297 + if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) { 1.298 + return true; 1.299 + } 1.300 + return false; 1.301 + } 1.302 + 1.303 + @Override 1.304 + protected void onAttachedToWindow() { 1.305 + // This check should not be done before the view is attached to a window 1.306 + // as hardware acceleration will not be enabled at that point. 1.307 + // We must create and add the SurfaceView instance before the view tree 1.308 + // is fully created to avoid flickering (see bug 801477). 1.309 + if (shouldUseTextureView()) { 1.310 + mTextureView = new TextureView(getContext()); 1.311 + mTextureView.setSurfaceTextureListener(new SurfaceTextureListener()); 1.312 + 1.313 + // The background is set to this color when the LayerView is 1.314 + // created, and it will be shown immediately at startup. Shortly 1.315 + // after, the tab's background color will be used before any content 1.316 + // is shown. 1.317 + mTextureView.setBackgroundColor(Color.WHITE); 1.318 + addView(mTextureView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 1.319 + } else { 1.320 + // This will stop PropertyAnimator from creating a drawing cache (i.e. a bitmap) 1.321 + // from a SurfaceView, which is just not possible (the bitmap will be transparent). 1.322 + setWillNotCacheDrawing(false); 1.323 + 1.324 + mSurfaceView = new LayerSurfaceView(getContext(), this); 1.325 + mSurfaceView.setBackgroundColor(Color.WHITE); 1.326 + addView(mSurfaceView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 1.327 + 1.328 + SurfaceHolder holder = mSurfaceView.getHolder(); 1.329 + holder.addCallback(new SurfaceListener()); 1.330 + } 1.331 + } 1.332 + 1.333 + // Don't expose GeckoLayerClient to things outside this package; only expose it as an Object 1.334 + GeckoLayerClient getLayerClient() { return mLayerClient; } 1.335 + public Object getLayerClientObject() { return mLayerClient; } 1.336 + 1.337 + public PanZoomController getPanZoomController() { return mPanZoomController; } 1.338 + public LayerMarginsAnimator getLayerMarginsAnimator() { return mMarginsAnimator; } 1.339 + 1.340 + public ImmutableViewportMetrics getViewportMetrics() { 1.341 + return mLayerClient.getViewportMetrics(); 1.342 + } 1.343 + 1.344 + public void abortPanning() { 1.345 + if (mPanZoomController != null) { 1.346 + mPanZoomController.abortPanning(); 1.347 + } 1.348 + } 1.349 + 1.350 + public PointF convertViewPointToLayerPoint(PointF viewPoint) { 1.351 + return mLayerClient.convertViewPointToLayerPoint(viewPoint); 1.352 + } 1.353 + 1.354 + int getBackgroundColor() { 1.355 + return mBackgroundColor; 1.356 + } 1.357 + 1.358 + @Override 1.359 + public void setBackgroundColor(int newColor) { 1.360 + mBackgroundColor = newColor; 1.361 + requestRender(); 1.362 + } 1.363 + 1.364 + public void setZoomConstraints(ZoomConstraints constraints) { 1.365 + mLayerClient.setZoomConstraints(constraints); 1.366 + } 1.367 + 1.368 + public void setIsRTL(boolean aIsRTL) { 1.369 + mLayerClient.setIsRTL(aIsRTL); 1.370 + } 1.371 + 1.372 + public void setInputConnectionHandler(InputConnectionHandler inputConnectionHandler) { 1.373 + mInputConnectionHandler = inputConnectionHandler; 1.374 + mLayerClient.forceRedraw(null); 1.375 + } 1.376 + 1.377 + @Override 1.378 + public Handler getHandler() { 1.379 + if (mInputConnectionHandler != null) 1.380 + return mInputConnectionHandler.getHandler(super.getHandler()); 1.381 + return super.getHandler(); 1.382 + } 1.383 + 1.384 + @Override 1.385 + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 1.386 + if (mInputConnectionHandler != null) 1.387 + return mInputConnectionHandler.onCreateInputConnection(outAttrs); 1.388 + return null; 1.389 + } 1.390 + 1.391 + @Override 1.392 + public boolean onKeyPreIme(int keyCode, KeyEvent event) { 1.393 + if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyPreIme(keyCode, event)) { 1.394 + return true; 1.395 + } 1.396 + return false; 1.397 + } 1.398 + 1.399 + @Override 1.400 + public boolean onKeyDown(int keyCode, KeyEvent event) { 1.401 + if (mPanZoomController != null && mPanZoomController.onKeyEvent(event)) { 1.402 + return true; 1.403 + } 1.404 + if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyDown(keyCode, event)) { 1.405 + return true; 1.406 + } 1.407 + return false; 1.408 + } 1.409 + 1.410 + @Override 1.411 + public boolean onKeyLongPress(int keyCode, KeyEvent event) { 1.412 + if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyLongPress(keyCode, event)) { 1.413 + return true; 1.414 + } 1.415 + return false; 1.416 + } 1.417 + 1.418 + @Override 1.419 + public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { 1.420 + if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyMultiple(keyCode, repeatCount, event)) { 1.421 + return true; 1.422 + } 1.423 + return false; 1.424 + } 1.425 + 1.426 + @Override 1.427 + public boolean onKeyUp(int keyCode, KeyEvent event) { 1.428 + if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyUp(keyCode, event)) { 1.429 + return true; 1.430 + } 1.431 + return false; 1.432 + } 1.433 + 1.434 + public boolean isIMEEnabled() { 1.435 + if (mInputConnectionHandler != null) { 1.436 + return mInputConnectionHandler.isIMEEnabled(); 1.437 + } 1.438 + return false; 1.439 + } 1.440 + 1.441 + public void requestRender() { 1.442 + if (mListener != null) { 1.443 + mListener.renderRequested(); 1.444 + } 1.445 + } 1.446 + 1.447 + public void addLayer(Layer layer) { 1.448 + mRenderer.addLayer(layer); 1.449 + } 1.450 + 1.451 + public void removeLayer(Layer layer) { 1.452 + mRenderer.removeLayer(layer); 1.453 + } 1.454 + 1.455 + public void postRenderTask(RenderTask task) { 1.456 + mRenderer.postRenderTask(task); 1.457 + } 1.458 + 1.459 + public void removeRenderTask(RenderTask task) { 1.460 + mRenderer.removeRenderTask(task); 1.461 + } 1.462 + 1.463 + public int getMaxTextureSize() { 1.464 + return mRenderer.getMaxTextureSize(); 1.465 + } 1.466 + 1.467 + /** Used by robocop for testing purposes. Not for production use! */ 1.468 + @RobocopTarget 1.469 + public IntBuffer getPixels() { 1.470 + return mRenderer.getPixels(); 1.471 + } 1.472 + 1.473 + /* paintState must be a PAINT_xxx constant. */ 1.474 + public void setPaintState(int paintState) { 1.475 + mPaintState = paintState; 1.476 + } 1.477 + 1.478 + public int getPaintState() { 1.479 + return mPaintState; 1.480 + } 1.481 + 1.482 + public LayerRenderer getRenderer() { 1.483 + return mRenderer; 1.484 + } 1.485 + 1.486 + public void setListener(Listener listener) { 1.487 + mListener = listener; 1.488 + } 1.489 + 1.490 + Listener getListener() { 1.491 + return mListener; 1.492 + } 1.493 + 1.494 + public GLController getGLController() { 1.495 + return mGLController; 1.496 + } 1.497 + 1.498 + private Bitmap getDrawable(String name) { 1.499 + BitmapFactory.Options options = new BitmapFactory.Options(); 1.500 + options.inScaled = false; 1.501 + Context context = getContext(); 1.502 + int resId = context.getResources().getIdentifier(name, "drawable", context.getPackageName()); 1.503 + return BitmapUtils.decodeResource(context, resId, options); 1.504 + } 1.505 + 1.506 + Bitmap getScrollbarImage() { 1.507 + return getDrawable("scrollbar"); 1.508 + } 1.509 + 1.510 + /* When using a SurfaceView (mSurfaceView != null), resizing happens in two 1.511 + * phases. First, the LayerView changes size, then, often some frames later, 1.512 + * the SurfaceView changes size. Because of this, we need to split the 1.513 + * resize into two phases to avoid jittering. 1.514 + * 1.515 + * The first phase is the LayerView size change. mListener is notified so 1.516 + * that a synchronous draw can be performed (otherwise a blank frame will 1.517 + * appear). 1.518 + * 1.519 + * The second phase is the SurfaceView size change. At this point, the 1.520 + * backing GL surface is resized and another synchronous draw is performed. 1.521 + * Gecko is also sent the new window size, and this will likely cause an 1.522 + * extra draw a few frames later, after it's re-rendered and caught up. 1.523 + * 1.524 + * In the case that there is no valid GL surface (for example, when 1.525 + * resuming, or when coming back from the awesomescreen), or we're using a 1.526 + * TextureView instead of a SurfaceView, the first phase is skipped. 1.527 + */ 1.528 + private void onSizeChanged(int width, int height) { 1.529 + if (!mGLController.isCompositorCreated()) { 1.530 + return; 1.531 + } 1.532 + 1.533 + surfaceChanged(width, height); 1.534 + 1.535 + if (mSurfaceView == null) { 1.536 + return; 1.537 + } 1.538 + 1.539 + if (mListener != null) { 1.540 + mListener.sizeChanged(width, height); 1.541 + } 1.542 + 1.543 + if (mOverscroll != null) { 1.544 + mOverscroll.setSize(width, height); 1.545 + } 1.546 + } 1.547 + 1.548 + private void surfaceChanged(int width, int height) { 1.549 + mGLController.serverSurfaceChanged(width, height); 1.550 + 1.551 + if (mListener != null) { 1.552 + mListener.surfaceChanged(width, height); 1.553 + } 1.554 + 1.555 + if (mOverscroll != null) { 1.556 + mOverscroll.setSize(width, height); 1.557 + } 1.558 + } 1.559 + 1.560 + private void onDestroyed() { 1.561 + mGLController.serverSurfaceDestroyed(); 1.562 + } 1.563 + 1.564 + public Object getNativeWindow() { 1.565 + if (mSurfaceView != null) 1.566 + return mSurfaceView.getHolder(); 1.567 + 1.568 + return mTextureView.getSurfaceTexture(); 1.569 + } 1.570 + 1.571 + @WrapElementForJNI(allowMultithread = true, stubName = "RegisterCompositorWrapper") 1.572 + public static GLController registerCxxCompositor() { 1.573 + try { 1.574 + LayerView layerView = GeckoAppShell.getLayerView(); 1.575 + GLController controller = layerView.getGLController(); 1.576 + controller.compositorCreated(); 1.577 + return controller; 1.578 + } catch (Exception e) { 1.579 + Log.e(LOGTAG, "Error registering compositor!", e); 1.580 + return null; 1.581 + } 1.582 + } 1.583 + 1.584 + public interface Listener { 1.585 + void renderRequested(); 1.586 + void sizeChanged(int width, int height); 1.587 + void surfaceChanged(int width, int height); 1.588 + } 1.589 + 1.590 + private class SurfaceListener implements SurfaceHolder.Callback { 1.591 + @Override 1.592 + public void surfaceChanged(SurfaceHolder holder, int format, int width, 1.593 + int height) { 1.594 + onSizeChanged(width, height); 1.595 + } 1.596 + 1.597 + @Override 1.598 + public void surfaceCreated(SurfaceHolder holder) { 1.599 + } 1.600 + 1.601 + @Override 1.602 + public void surfaceDestroyed(SurfaceHolder holder) { 1.603 + onDestroyed(); 1.604 + } 1.605 + } 1.606 + 1.607 + /* A subclass of SurfaceView to listen to layout changes, as 1.608 + * View.OnLayoutChangeListener requires API level 11. 1.609 + */ 1.610 + private class LayerSurfaceView extends SurfaceView { 1.611 + LayerView mParent; 1.612 + 1.613 + public LayerSurfaceView(Context aContext, LayerView aParent) { 1.614 + super(aContext); 1.615 + mParent = aParent; 1.616 + } 1.617 + 1.618 + @Override 1.619 + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 1.620 + if (changed) { 1.621 + mParent.surfaceChanged(right - left, bottom - top); 1.622 + } 1.623 + } 1.624 + } 1.625 + 1.626 + private class SurfaceTextureListener implements TextureView.SurfaceTextureListener { 1.627 + @Override 1.628 + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 1.629 + // We don't do this for surfaceCreated above because it is always followed by a surfaceChanged, 1.630 + // but that is not the case here. 1.631 + onSizeChanged(width, height); 1.632 + } 1.633 + 1.634 + @Override 1.635 + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 1.636 + onDestroyed(); 1.637 + return true; // allow Android to call release() on the SurfaceTexture, we are done drawing to it 1.638 + } 1.639 + 1.640 + @Override 1.641 + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 1.642 + onSizeChanged(width, height); 1.643 + } 1.644 + 1.645 + @Override 1.646 + public void onSurfaceTextureUpdated(SurfaceTexture surface) { 1.647 + 1.648 + } 1.649 + } 1.650 + 1.651 + @RobocopTarget 1.652 + public void addDrawListener(DrawListener listener) { 1.653 + mLayerClient.addDrawListener(listener); 1.654 + } 1.655 + 1.656 + @RobocopTarget 1.657 + public void removeDrawListener(DrawListener listener) { 1.658 + mLayerClient.removeDrawListener(listener); 1.659 + } 1.660 + 1.661 + @RobocopTarget 1.662 + public static interface DrawListener { 1.663 + public void drawFinished(); 1.664 + } 1.665 + 1.666 + @Override 1.667 + public void setOverScrollMode(int overscrollMode) { 1.668 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { 1.669 + super.setOverScrollMode(overscrollMode); 1.670 + } 1.671 + if (mPanZoomController != null) { 1.672 + mPanZoomController.setOverScrollMode(overscrollMode); 1.673 + } 1.674 + } 1.675 + 1.676 + @Override 1.677 + public int getOverScrollMode() { 1.678 + if (mPanZoomController != null) { 1.679 + return mPanZoomController.getOverScrollMode(); 1.680 + } 1.681 + 1.682 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { 1.683 + return super.getOverScrollMode(); 1.684 + } 1.685 + return View.OVER_SCROLL_ALWAYS; 1.686 + } 1.687 + 1.688 + @Override 1.689 + public void onFocusChanged (boolean gainFocus, int direction, Rect previouslyFocusedRect) { 1.690 + super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 1.691 + GeckoAccessibility.onLayerViewFocusChanged(this, gainFocus); 1.692 + } 1.693 + 1.694 + public void setFullScreen(boolean fullScreen) { 1.695 + mFullScreen = fullScreen; 1.696 + } 1.697 + 1.698 + public boolean isFullScreen() { 1.699 + return mFullScreen; 1.700 + } 1.701 + 1.702 + @Override 1.703 + public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { 1.704 + if (msg == Tabs.TabEvents.VIEWPORT_CHANGE && Tabs.getInstance().isSelectedTab(tab) && mLayerClient != null) { 1.705 + setZoomConstraints(tab.getZoomConstraints()); 1.706 + setIsRTL(tab.getIsRTL()); 1.707 + } 1.708 + } 1.709 + 1.710 + // Public hooks for listening to metrics changing 1.711 + 1.712 + public interface OnMetricsChangedListener { 1.713 + public void onMetricsChanged(ImmutableViewportMetrics viewport); 1.714 + public void onPanZoomStopped(); 1.715 + } 1.716 + 1.717 + public void setOnMetricsChangedListener(OnMetricsChangedListener listener) { 1.718 + mLayerClient.setOnMetricsChangedListener(listener); 1.719 + } 1.720 +}