diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /android/source/src/java/org/mozilla/gecko/gfx/LayerView.java | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'android/source/src/java/org/mozilla/gecko/gfx/LayerView.java')
-rw-r--r-- | android/source/src/java/org/mozilla/gecko/gfx/LayerView.java | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/LayerView.java b/android/source/src/java/org/mozilla/gecko/gfx/LayerView.java new file mode 100644 index 000000000..05f211811 --- /dev/null +++ b/android/source/src/java/org/mozilla/gecko/gfx/LayerView.java @@ -0,0 +1,449 @@ +/* -*- 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 android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.PixelFormat; +import android.graphics.SurfaceTexture; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.TextureView; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.widget.FrameLayout; + +import org.libreoffice.LOEvent; +import org.libreoffice.LOKitShell; +import org.libreoffice.LibreOfficeMainActivity; +import org.libreoffice.R; +import org.mozilla.gecko.OnInterceptTouchListener; +import org.mozilla.gecko.OnSlideSwipeListener; + +/** + * 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 FrameLayout { + private static String LOGTAG = LayerView.class.getName(); + + private GeckoLayerClient mLayerClient; + private PanZoomController mPanZoomController; + private GLController mGLController; + private InputConnectionHandler mInputConnectionHandler; + private LayerRenderer mRenderer; + + /* Must be a PAINT_xxx constant */ + private int mPaintState = PAINT_NONE; + private boolean mFullScreen = false; + + private SurfaceView mSurfaceView; + private TextureView mTextureView; + + private Listener mListener; + private OnInterceptTouchListener mTouchIntercepter; + //TODO static because of registerCxxCompositor() function, should be fixed in the future + private static LibreOfficeMainActivity mContext; + + /* 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; + + boolean shouldUseTextureView() { + // we can only use TextureView on ICS or higher + /*if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + Log.i(LOGTAG, "Not using TextureView: not on ICS+"); + return false; + } + + try { + // and then we can only use it if we have a hardware accelerated window + Method m = View.class.getMethod("isHardwareAccelerated", new Class[0]); + return (Boolean) m.invoke(this); + } catch (Exception e) { + Log.i(LOGTAG, "Not using TextureView: caught exception checking for hw accel: " + e.toString()); + return false; + }*/ + return false; + } + + public LayerView(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = (LibreOfficeMainActivity) context; + + if (shouldUseTextureView()) { + mTextureView = new TextureView(context); + mTextureView.setSurfaceTextureListener(new SurfaceTextureListener()); + + addView(mTextureView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + } else { + mSurfaceView = new SurfaceView(context); + addView(mSurfaceView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + + SurfaceHolder holder = mSurfaceView.getHolder(); + holder.addCallback(new SurfaceListener()); + holder.setFormat(PixelFormat.RGB_565); + } + + mGLController = new GLController(this); + } + + void connect(GeckoLayerClient layerClient) { + mLayerClient = layerClient; + mPanZoomController = mLayerClient.getPanZoomController(); + mRenderer = new LayerRenderer(this); + mInputConnectionHandler = null; + + setFocusable(true); + setFocusableInTouchMode(true); + + createGLThread(); + setOnTouchListener(new OnSlideSwipeListener(getContext(), mLayerClient)); + } + + public void show() { + // Fix this if TextureView support is turned back on above + mSurfaceView.setVisibility(View.VISIBLE); + } + + public void hide() { + // Fix this if TextureView support is turned back on above + mSurfaceView.setVisibility(View.INVISIBLE); + } + + public void destroy() { + if (mLayerClient != null) { + mLayerClient.destroy(); + } + if (mRenderer != null) { + mRenderer.destroy(); + } + } + + public void setTouchIntercepter(final OnInterceptTouchListener touchIntercepter) { + // this gets run on the gecko thread, but for thread safety we want the assignment + // on the UI thread. + post(new Runnable() { + public void run() { + mTouchIntercepter = touchIntercepter; + } + }); + } + + public void setInputConnectionHandler(InputConnectionHandler inputConnectionHandler) { + mInputConnectionHandler = inputConnectionHandler; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + requestFocus(); + } + + if (mTouchIntercepter != null && mTouchIntercepter.onInterceptTouchEvent(this, event)) { + return true; + } + if (mPanZoomController != null && mPanZoomController.onTouchEvent(event)) { + return true; + } + if (mTouchIntercepter != null && mTouchIntercepter.onTouch(this, event)) { + return true; + } + return false; + } + + @Override + public boolean onHoverEvent(MotionEvent event) { + return mTouchIntercepter != null && mTouchIntercepter.onTouch(this, event); + } + + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + return mPanZoomController != null && mPanZoomController.onMotionEvent(event); + } + + public GeckoLayerClient getLayerClient() { return mLayerClient; } + public PanZoomController getPanZoomController() { return mPanZoomController; } + + public void setViewportSize(IntSize size) { + mLayerClient.setViewportSize(new FloatSize(size)); + } + + public ImmutableViewportMetrics getViewportMetrics() { + return mLayerClient.getViewportMetrics(); + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + if (mInputConnectionHandler != null) + return mInputConnectionHandler.onCreateInputConnection(outAttrs); + return null; + } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + return mInputConnectionHandler != null && mInputConnectionHandler.onKeyPreIme(keyCode, event); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return mInputConnectionHandler != null && mInputConnectionHandler.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + return mInputConnectionHandler != null && mInputConnectionHandler.onKeyLongPress(keyCode, event); + } + + @Override + public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { + return mInputConnectionHandler != null && mInputConnectionHandler.onKeyMultiple(keyCode, repeatCount, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + return mInputConnectionHandler != null && mInputConnectionHandler.onKeyUp(keyCode, event); + } + + public boolean isIMEEnabled() { + /*if (mInputConnectionHandler != null) { + return mInputConnectionHandler.isIMEEnabled(); + }*/ + 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(); + } + + 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; + } + + public Bitmap getDrawable(String name) { + Context context = getContext(); + Resources resources = context.getResources(); + String packageName = resources.getResourcePackageName(R.id.dummy_id_for_package_name_resolution); + int resourceID = resources.getIdentifier(name, "drawable", packageName); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inScaled = false; + return BitmapFactory.decodeResource(context.getResources(), resourceID, options); + } + + Bitmap getBackgroundPattern() { + return getDrawable("background"); + } + + Bitmap getShadowPattern() { + return getDrawable("shadow"); + } + + private void onSizeChanged(int width, int height) { + mGLController.surfaceChanged(width, height); + + mLayerClient.setViewportSize(new FloatSize(width, height)); + + if (mListener != null) { + mListener.surfaceChanged(width, height); + } + + LOKitShell.sendEvent(new LOEvent(LOEvent.UPDATE_ZOOM_CONSTRAINTS)); + } + + private void onDestroyed() { + mGLController.surfaceDestroyed(); + + if (mListener != null) { + mListener.compositionPauseRequested(); + } + } + + public Object getNativeWindow() { + if (mSurfaceView != null) + return mSurfaceView.getHolder(); + + return mTextureView.getSurfaceTexture(); + } + + /** This function is invoked by Gecko (compositor thread) via JNI; be careful when modifying signature. */ + public static GLController registerCxxCompositor() { + try { + LayerView layerView = mContext.getLayerClient().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); + } + + private class SurfaceListener implements SurfaceHolder.Callback { + public void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) { + onSizeChanged(width, height); + } + + public void surfaceCreated(SurfaceHolder holder) { + if (mRenderControllerThread != null) { + mRenderControllerThread.surfaceCreated(); + } + } + + public void surfaceDestroyed(SurfaceHolder holder) { + onDestroyed(); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (changed) { + setViewportSize(new IntSize(right - left, bottom - top)); + } + } + + private class SurfaceTextureListener implements TextureView.SurfaceTextureListener { + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + // We don't do this for surfaceCreated above because it is always followed by a surfaceChanged, + // but that is not the case here. + if (mRenderControllerThread != null) { + mRenderControllerThread.surfaceCreated(); + } + onSizeChanged(width, height); + } + + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + onDestroyed(); + return true; // allow Android to call release() on the SurfaceTexture, we are done drawing to it + } + + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + onSizeChanged(width, height); + } + + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + } + } + + private RenderControllerThread mRenderControllerThread; + + public synchronized void createGLThread() { + if (mRenderControllerThread != null) { + throw new LayerViewException ("createGLThread() called with a GL thread already in place!"); + } + + Log.e(LOGTAG, "### Creating GL thread!"); + mRenderControllerThread = new RenderControllerThread(mGLController); + mRenderControllerThread.start(); + setListener(mRenderControllerThread); + notifyAll(); + } + + public synchronized Thread destroyGLThread() { + // Wait for the GL thread to be started. + Log.e(LOGTAG, "### Waiting for GL thread to be created..."); + while (mRenderControllerThread == null) { + try { + wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + Log.e(LOGTAG, "### Destroying GL thread!"); + Thread thread = mRenderControllerThread; + mRenderControllerThread.shutdown(); + setListener(null); + mRenderControllerThread = null; + return thread; + } + + public static class LayerViewException extends RuntimeException { + public static final long serialVersionUID = 1L; + + LayerViewException(String e) { + super(e); + } + } + + public void setFullScreen(boolean fullScreen) { + mFullScreen = fullScreen; + } + + public boolean isFullScreen() { + return mFullScreen; + } +} |