/*******************************************************************************
* Copyright (c) 2015 - 2017
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package go.graphics.android;
import java.lang.reflect.Method;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;
import go.graphics.GLDrawContext;
import go.graphics.RedrawListener;
import go.graphics.UIPoint;
import go.graphics.area.Area;
import go.graphics.event.GOEvent;
import go.graphics.event.GOEventHandlerProvider;
import go.graphics.event.interpreter.AbstractEventConverter;
import android.content.Context;
import android.opengl.GLES10;
import android.opengl.GLSurfaceView;
import android.os.Vibrator;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
public class GOSurfaceView extends GLSurfaceView implements RedrawListener, GOEventHandlerProvider {
private final Area area;
private final ActionAdapter actionAdapter = new ActionAdapter(getContext(), this);
private AndroidContext drawcontext;
private IContextDestroyedListener contextDestroyedListener = null;
public GOSurfaceView(Context context, Area area) {
super(context);
this.area = area;
setEGLContextFactory(new Factory());
setRenderer(new Renderer(context));
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
tryEnableContextPreservation();
area.addRedrawListener(this);
}
private void tryEnableContextPreservation() {
// api level 11 :-(
// super.setPreserveEGLContextOnPause(true);
try {
Method m = GLSurfaceView.class.getMethod("setPreserveEGLContextOnPause", Boolean.TYPE);
m.invoke(this, true);
} catch (Throwable t) {
Log.d("gl", "Could not enable context preservation");
}
}
@Override
public boolean onTouchEvent(MotionEvent e) {
actionAdapter.onTouchEvent(e);
return true;
}
@Override
public void onPause() {
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
super.onPause();
}
@Override
public void onResume() {
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
super.onResume();
}
private class ActionAdapter extends AbstractEventConverter {
protected ActionAdapter(Context context, GOEventHandlerProvider provider) {
super(provider);
gestureDetector = new GestureDetector(context, gestureListener);
longPressDetector = new GestureDetector(context, longPressListener);
scaleGestureDetector = new ScaleGestureDetector(context, scaleGestureListener);
gestureDetector.setIsLongpressEnabled(false);
vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
}
private final Vibrator vibrator;
/**
* The pan start center, in GO space
*/
private UIPoint panStart = new UIPoint(0, 0);
private final GestureDetector longPressDetector;
private final GestureDetector.SimpleOnGestureListener longPressListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent e) {
vibrator.vibrate(25);
endPan(currentPoint(e));
startDraw(currentPoint(e));
}
};
private final GestureDetector gestureDetector;
private final GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (drawStarted()) {
abortDraw();
fireMoveTo(e);
} else {
fireSelectPoint(e);
}
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (drawStarted()) {
updateDrawPosition(currentPoint(e2));
} else if (panStarted()) {
updatePanPosition(relativePanPoint(e2));
} else {
panStart = currentPoint(e2);
startPan(new UIPoint(0, 0));
}
return true;
}
};
private final ScaleGestureDetector scaleGestureDetector;
private final ScaleGestureDetector.OnScaleGestureListener scaleGestureListener = new ScaleGestureDetector.OnScaleGestureListener() {
private float startSpan;
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
startSpan = detector.getCurrentSpan();
startZoom();
return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
updateZoomFactor(detector.getCurrentSpan() / startSpan);
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
endZoomEvent(detector.getCurrentSpan() / startSpan, null);
}
};
public void onTouchEvent(MotionEvent e) {
scaleGestureDetector.onTouchEvent(e);
gestureDetector.onTouchEvent(e);
longPressDetector.onTouchEvent(e);
if (e.getAction() == MotionEvent.ACTION_UP) {
if (drawStarted()) {
endDraw(currentPoint(e));
}
if (panStarted()) {
endPan(relativePanPoint(e));
}
}
if (e.getPointerCount() > 1) {
if (drawStarted()) {
abortDraw();
}
if (panStarted()) {
endPan(relativePanPoint(e));
}
}
}
private UIPoint relativePanPoint(MotionEvent e) {
return new UIPoint(e.getX() - panStart.getX(), getHeight() - e.getY() - panStart.getY());
}
private UIPoint currentPoint(MotionEvent e) {
return new UIPoint(e.getX(), getHeight() - e.getY());
}
private void fireSelectPoint(MotionEvent e) {
fireCommandEvent(new UIPoint(e.getX(), getHeight() - e.getY()), true);
}
private void fireMoveTo(MotionEvent e) {
fireCommandEvent(new UIPoint(e.getX(), getHeight() - e.getY()), false);
}
}
private class Renderer implements GLSurfaceView.Renderer {
private Renderer(Context aContext) {
drawcontext = new AndroidContext(aContext);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES10.glClearColor(0, 0, 0, 1);
GLES10.glClear(GL10.GL_DEPTH_BUFFER_BIT | GL10.GL_COLOR_BUFFER_BIT);
GLES10.glDepthFunc(GLES10.GL_LEQUAL);
GLES10.glEnable(GLES10.GL_DEPTH_TEST);
area.drawArea(drawcontext);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
area.setWidth(width);
area.setHeight(height);
drawcontext.reinit(width, height);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}
}
private class Factory implements EGLContextFactory {
@Override
public EGLContext createContext(EGL10 arg0, EGLDisplay display, EGLConfig config) {
int[] attributes = new int[] { EGL10.EGL_NONE };
return arg0.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
attributes);
}
@Override
public void destroyContext(EGL10 arg0, EGLDisplay arg1, EGLContext arg2) {
Log.w("gl", "Invalidating texture context");
drawcontext.invalidateContext();
AndroidTextDrawer.invalidateTextures();
IContextDestroyedListener listener = contextDestroyedListener;
if (listener != null) {
listener.glContextDestroyed();
}
arg0.eglDestroyContext(arg1, arg2);
}
}
@Override
public void requestRedraw() {
requestRender();
}
@Override
public void handleEvent(GOEvent event) {
area.handleEvent(event);
}
public GLDrawContext getDrawContext() {
return drawcontext;
}
public void setContextDestroyedListener(IContextDestroyedListener contextDestroyedListener) {
this.contextDestroyedListener = contextDestroyedListener;
}
}