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