/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.gfx; import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.GeckoInputConnection; import android.content.Context; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.util.Log; import android.graphics.PixelFormat; import android.view.SurfaceHolder; import android.view.SurfaceView; import java.nio.IntBuffer; /** * A view rendered by the layer compositor. * * This view delegates to LayerRenderer to actually do the drawing. Its role is largely that of a * mediator between the LayerRenderer and the LayerController. * * Note that LayerView is accessed by Robocop via reflection. */ public class LayerView extends SurfaceView implements SurfaceHolder.Callback { private static String LOGTAG = "GeckoLayerView"; private Context mContext; private LayerController mController; private TouchEventHandler mTouchEventHandler; private GLController mGLController; private InputConnectionHandler mInputConnectionHandler; private LayerRenderer mRenderer; /* Must be a PAINT_xxx constant */ private int mPaintState = PAINT_NONE; private Listener mListener; /* Flags used to determine when to show the painted surface. The integer * order must correspond to the order in which these states occur. */ public static final int PAINT_NONE = 0; public static final int PAINT_BEFORE_FIRST = 1; public static final int PAINT_AFTER_FIRST = 2; public LayerView(Context context, LayerController controller) { super(context); SurfaceHolder holder = getHolder(); holder.addCallback(this); holder.setFormat(PixelFormat.RGB_565); mGLController = new GLController(this); mContext = context; mController = controller; mTouchEventHandler = new TouchEventHandler(context, this, mController); mRenderer = new LayerRenderer(this); mInputConnectionHandler = null; setFocusable(true); setFocusableInTouchMode(true); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) requestFocus(); /** We need to manually hide FormAssistPopup because it is not a regular PopupWindow. */ if (GeckoApp.mAppContext != null) GeckoApp.mAppContext.hideFormAssistPopup(); return mTouchEventHandler.handleEvent(event); } @Override public boolean onHoverEvent(MotionEvent event) { return mTouchEventHandler.handleEvent(event); } public LayerController getController() { return mController; } public TouchEventHandler getTouchEventHandler() { return mTouchEventHandler; } /** The LayerRenderer calls this to indicate that the window has changed size. */ public void setViewportSize(IntSize size) { mController.setViewportSize(new FloatSize(size)); } public GeckoInputConnection setInputConnectionHandler() { GeckoInputConnection geckoInputConnection = GeckoInputConnection.create(this); mInputConnectionHandler = geckoInputConnection; return geckoInputConnection; } @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { if (mInputConnectionHandler != null) return mInputConnectionHandler.onCreateInputConnection(outAttrs); return null; } @Override public boolean onKeyPreIme(int keyCode, KeyEvent event) { if (mInputConnectionHandler != null) return mInputConnectionHandler.onKeyPreIme(keyCode, event); return false; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (mInputConnectionHandler != null) return mInputConnectionHandler.onKeyDown(keyCode, event); return false; } @Override public boolean onKeyLongPress(int keyCode, KeyEvent event) { if (mInputConnectionHandler != null) return mInputConnectionHandler.onKeyLongPress(keyCode, event); return false; } @Override public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { if (mInputConnectionHandler != null) return mInputConnectionHandler.onKeyMultiple(keyCode, repeatCount, event); return false; } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (mInputConnectionHandler != null) return mInputConnectionHandler.onKeyUp(keyCode, event); return false; } public void requestRender() { if (mListener != null) { mListener.renderRequested(); } } public void addLayer(Layer layer) { mRenderer.addLayer(layer); } public void removeLayer(Layer layer) { mRenderer.removeLayer(layer); } public int getMaxTextureSize() { return mRenderer.getMaxTextureSize(); } /** Used by robocop for testing purposes. Not for production use! This is called via reflection by robocop. */ public IntBuffer getPixels() { return mRenderer.getPixels(); } public void setLayerRenderer(LayerRenderer renderer) { mRenderer = renderer; } public LayerRenderer getLayerRenderer() { return mRenderer; } /* paintState must be a PAINT_xxx constant. The state will only be changed * if paintState represents a state that occurs after the current state. */ public void setPaintState(int paintState) { if (paintState > mPaintState) { Log.d(LOGTAG, "LayerView paint state set to " + paintState); mPaintState = paintState; } } public int getPaintState() { return mPaintState; } public LayerRenderer getRenderer() { return mRenderer; } public void setListener(Listener listener) { mListener = listener; } Listener getListener() { return mListener; } public GLController getGLController() { return mGLController; } /** Implementation of SurfaceHolder.Callback */ public synchronized void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mGLController.surfaceChanged(width, height); if (mListener != null) { mListener.surfaceChanged(width, height); } } /** Implementation of SurfaceHolder.Callback */ public synchronized void surfaceCreated(SurfaceHolder holder) { } /** Implementation of SurfaceHolder.Callback */ public synchronized void surfaceDestroyed(SurfaceHolder holder) { mGLController.surfaceDestroyed(); if (mListener != null) { mListener.compositionPauseRequested(); } } /** This function is invoked by Gecko (compositor thread) via JNI; be careful when modifying signature. */ public static GLController registerCxxCompositor() { try { LayerView layerView = GeckoApp.mAppContext.getLayerController().getView(); layerView.mListener.compositorCreated(); return layerView.getGLController(); } catch (Exception e) { Log.e(LOGTAG, "Error registering compositor!", e); return null; } } public interface Listener { void compositorCreated(); void renderRequested(); void compositionPauseRequested(); void compositionResumeRequested(int width, int height); void surfaceChanged(int width, int height); } }