| @@ -114,7 +114,10 @@ public final class JuceDemo extends Activity | |||
| public final void deleteView (ComponentPeerView view) | |||
| { | |||
| viewHolder.removeView (view); | |||
| ViewGroup group = (ViewGroup) (view.getParent()); | |||
| if (group != null) | |||
| group.removeView (view); | |||
| } | |||
| final class ViewHolder extends ViewGroup | |||
| @@ -246,6 +249,7 @@ public final class JuceDemo extends Activity | |||
| public ComponentPeerView (Context context, boolean opaque_) | |||
| { | |||
| super (context); | |||
| setWillNotDraw (false); | |||
| opaque = opaque_; | |||
| setFocusable (true); | |||
| @@ -260,6 +264,7 @@ public final class JuceDemo extends Activity | |||
| @Override | |||
| public void draw (Canvas canvas) | |||
| { | |||
| super.draw (canvas); | |||
| handlePaint (canvas); | |||
| } | |||
| @@ -295,11 +300,16 @@ public final class JuceDemo extends Activity | |||
| @Override | |||
| protected void onSizeChanged (int w, int h, int oldw, int oldh) | |||
| { | |||
| super.onSizeChanged (w, h, oldw, oldh); | |||
| viewSizeChanged(); | |||
| } | |||
| @Override | |||
| protected void onLayout (boolean changed, int left, int top, int right, int bottom) {} | |||
| protected void onLayout (boolean changed, int left, int top, int right, int bottom) | |||
| { | |||
| for (int i = getChildCount(); --i >= 0;) | |||
| requestTransparentRegion (getChildAt (i)); | |||
| } | |||
| private native void viewSizeChanged(); | |||
| @@ -321,38 +331,59 @@ public final class JuceDemo extends Activity | |||
| { | |||
| return true; //xxx needs to check overlapping views | |||
| } | |||
| public OpenGLView createGLView() | |||
| { | |||
| OpenGLView glView = new OpenGLView (getContext()); | |||
| addView (glView); | |||
| return glView; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| public final class OpenGLView extends GLSurfaceView | |||
| implements GLSurfaceView.Renderer | |||
| public final class OpenGLView extends GLSurfaceView | |||
| implements GLSurfaceView.Renderer | |||
| { | |||
| public OpenGLView (ComponentPeerView parent) | |||
| OpenGLView (Context context) | |||
| { | |||
| super (parent.getContext()); | |||
| super (context); | |||
| setEGLContextClientVersion (2); | |||
| getHolder().setFormat(PixelFormat.TRANSLUCENT); | |||
| setVisibility (VISIBLE); | |||
| setRenderer (this); | |||
| parent.addView (this); | |||
| //setRenderMode (RENDERMODE_CONTINUOUSLY); | |||
| //requestRender(); | |||
| } | |||
| @Override | |||
| public void onSurfaceCreated (GL10 unused, EGLConfig config) | |||
| { | |||
| contextCreated(); | |||
| // contextCreated(); | |||
| } | |||
| @Override | |||
| public void onDrawFrame (GL10 unused) | |||
| { | |||
| GLES20.glClearColor (1.0f, 0.5f, 0.0f, 1.0f); | |||
| GLES20.glClear (GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); | |||
| //GLES20.glClearColor (1.0f, 0.5f, 0.0f, 1.0f); | |||
| //GLES20.glClear (GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); | |||
| render(); | |||
| // render(); | |||
| } | |||
| @Override | |||
| public void onSurfaceChanged (GL10 unused, int width, int height) | |||
| { | |||
| GLES20.glViewport (0, 0, width, height); | |||
| //GLES20.glViewport (0, 0, width, height); | |||
| } | |||
| @Override | |||
| protected void onLayout (boolean changed, int left, int top, int right, int bottom) | |||
| { | |||
| super.onLayout (changed, left, top, right, bottom); | |||
| requestLayout(); | |||
| ((ViewGroup) getParent()).requestTransparentRegion (this); | |||
| } | |||
| private native void contextCreated(); | |||
| @@ -32,6 +32,7 @@ import android.content.Context; | |||
| import android.os.Bundle; | |||
| import android.view.*; | |||
| import android.graphics.*; | |||
| import android.opengl.*; | |||
| import android.text.ClipboardManager; | |||
| import java.io.BufferedInputStream; | |||
| import java.io.IOException; | |||
| @@ -39,6 +40,8 @@ import java.io.InputStream; | |||
| import java.io.OutputStream; | |||
| import java.net.URL; | |||
| import java.net.HttpURLConnection; | |||
| import javax.microedition.khronos.egl.EGLConfig; | |||
| import javax.microedition.khronos.opengles.GL10; | |||
| //============================================================================== | |||
| public final class JuceAppActivity extends Activity | |||
| @@ -72,9 +75,9 @@ public final class JuceAppActivity extends Activity | |||
| } | |||
| //============================================================================== | |||
| public native void launchApp (String appFile, String appDataDir); | |||
| public native void quitApp(); | |||
| public native void setScreenSize (int screenWidth, int screenHeight); | |||
| private native void launchApp (String appFile, String appDataDir); | |||
| private native void quitApp(); | |||
| private native void setScreenSize (int screenWidth, int screenHeight); | |||
| //============================================================================== | |||
| public static final void printToConsole (String s) | |||
| @@ -111,7 +114,10 @@ public final class JuceAppActivity extends Activity | |||
| public final void deleteView (ComponentPeerView view) | |||
| { | |||
| viewHolder.removeView (view); | |||
| ViewGroup group = (ViewGroup) (view.getParent()); | |||
| if (group != null) | |||
| group.removeView (view); | |||
| } | |||
| final class ViewHolder extends ViewGroup | |||
| @@ -237,12 +243,13 @@ public final class JuceAppActivity extends Activity | |||
| public native void alertDismissed (long callback, int id); | |||
| //============================================================================== | |||
| public class ComponentPeerView extends View | |||
| implements View.OnFocusChangeListener | |||
| public final class ComponentPeerView extends ViewGroup | |||
| implements View.OnFocusChangeListener | |||
| { | |||
| public ComponentPeerView (Context context, boolean opaque_) | |||
| { | |||
| super (context); | |||
| setWillNotDraw (false); | |||
| opaque = opaque_; | |||
| setFocusable (true); | |||
| @@ -257,6 +264,7 @@ public final class JuceAppActivity extends Activity | |||
| @Override | |||
| public void draw (Canvas canvas) | |||
| { | |||
| super.draw (canvas); | |||
| handlePaint (canvas); | |||
| } | |||
| @@ -292,11 +300,16 @@ public final class JuceAppActivity extends Activity | |||
| @Override | |||
| protected void onSizeChanged (int w, int h, int oldw, int oldh) | |||
| { | |||
| super.onSizeChanged (w, h, oldw, oldh); | |||
| viewSizeChanged(); | |||
| } | |||
| @Override | |||
| protected void onLayout (boolean changed, int left, int top, int right, int bottom) {} | |||
| protected void onLayout (boolean changed, int left, int top, int right, int bottom) | |||
| { | |||
| for (int i = getChildCount(); --i >= 0;) | |||
| requestTransparentRegion (getChildAt (i)); | |||
| } | |||
| private native void viewSizeChanged(); | |||
| @@ -318,6 +331,63 @@ public final class JuceAppActivity extends Activity | |||
| { | |||
| return true; //xxx needs to check overlapping views | |||
| } | |||
| public OpenGLView createGLView() | |||
| { | |||
| OpenGLView glView = new OpenGLView (getContext()); | |||
| addView (glView); | |||
| return glView; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| public final class OpenGLView extends GLSurfaceView | |||
| implements GLSurfaceView.Renderer | |||
| { | |||
| OpenGLView (Context context) | |||
| { | |||
| super (context); | |||
| setEGLContextClientVersion (2); | |||
| getHolder().setFormat(PixelFormat.TRANSLUCENT); | |||
| setVisibility (VISIBLE); | |||
| setRenderer (this); | |||
| //setRenderMode (RENDERMODE_CONTINUOUSLY); | |||
| //requestRender(); | |||
| } | |||
| @Override | |||
| public void onSurfaceCreated (GL10 unused, EGLConfig config) | |||
| { | |||
| // contextCreated(); | |||
| } | |||
| @Override | |||
| public void onDrawFrame (GL10 unused) | |||
| { | |||
| //GLES20.glClearColor (1.0f, 0.5f, 0.0f, 1.0f); | |||
| //GLES20.glClear (GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); | |||
| // render(); | |||
| } | |||
| @Override | |||
| public void onSurfaceChanged (GL10 unused, int width, int height) | |||
| { | |||
| //GLES20.glViewport (0, 0, width, height); | |||
| } | |||
| @Override | |||
| protected void onLayout (boolean changed, int left, int top, int right, int bottom) | |||
| { | |||
| super.onLayout (changed, left, top, right, bottom); | |||
| requestLayout(); | |||
| ((ViewGroup) getParent()).requestTransparentRegion (this); | |||
| } | |||
| private native void contextCreated(); | |||
| private native void render(); | |||
| } | |||
| //============================================================================== | |||
| @@ -344,7 +414,7 @@ public final class JuceAppActivity extends Activity | |||
| c.setMatrix (matrix); | |||
| c.drawPath (p, paint); | |||
| int sizeNeeded = w * h; | |||
| final int sizeNeeded = w * h; | |||
| if (cachedRenderArray.length < sizeNeeded) | |||
| cachedRenderArray = new int [sizeNeeded]; | |||
| @@ -393,25 +463,10 @@ public final class JuceAppActivity extends Activity | |||
| return num; | |||
| } | |||
| public final long getPosition() | |||
| { | |||
| return position; | |||
| } | |||
| public final long getTotalLength() | |||
| { | |||
| return -1; | |||
| } | |||
| public final boolean isExhausted() | |||
| { | |||
| return false; | |||
| } | |||
| public final boolean setPosition (long newPos) | |||
| { | |||
| return false; | |||
| } | |||
| public final long getPosition() { return position; } | |||
| public final long getTotalLength() { return -1; } | |||
| public final boolean isExhausted() { return false; } | |||
| public final boolean setPosition (long newPos) { return false; } | |||
| private HttpURLConnection connection; | |||
| private InputStream inputStream; | |||
| @@ -419,7 +474,8 @@ public final class JuceAppActivity extends Activity | |||
| } | |||
| public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, | |||
| String headers, int timeOutMs, java.lang.StringBuffer responseHeaders) | |||
| String headers, int timeOutMs, | |||
| java.lang.StringBuffer responseHeaders) | |||
| { | |||
| try | |||
| { | |||
| @@ -55,14 +55,14 @@ void JNIClassBase::release (JNIEnv* env) | |||
| void JNIClassBase::initialiseAllClasses (JNIEnv* env) | |||
| { | |||
| Array<JNIClassBase*>& classes = getClasses(); | |||
| const Array<JNIClassBase*>& classes = getClasses(); | |||
| for (int i = classes.size(); --i >= 0;) | |||
| classes.getUnchecked(i)->initialise (env); | |||
| } | |||
| void JNIClassBase::releaseAllClasses (JNIEnv* env) | |||
| { | |||
| Array<JNIClassBase*>& classes = getClasses(); | |||
| const Array<JNIClassBase*>& classes = getClasses(); | |||
| for (int i = classes.size(); --i >= 0;) | |||
| classes.getUnchecked(i)->release (env); | |||
| } | |||
| @@ -119,7 +119,7 @@ public: | |||
| /** Returns the inverse of this point. */ | |||
| Point operator-() const noexcept { return Point (-x, -y); } | |||
| /** Returns the straight-line distance between this point and another one. */ | |||
| /** Returns the straight-line distance between this point and the origin. */ | |||
| ValueType getDistanceFromOrigin() const noexcept { return juce_hypot (x, y); } | |||
| /** Returns the straight-line distance between this point and another one. */ | |||
| @@ -78,6 +78,7 @@ DECLARE_JNI_CLASS (CanvasMinimal, "android/graphics/Canvas"); | |||
| METHOD (hasFocus, "hasFocus", "()Z") \ | |||
| METHOD (invalidate, "invalidate", "(IIII)V") \ | |||
| METHOD (containsPoint, "containsPoint", "(II)Z") \ | |||
| METHOD (createGLView, "createGLView", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$OpenGLView;") \ | |||
| DECLARE_JNI_CLASS (ComponentPeerView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| @@ -585,6 +586,11 @@ ComponentPeer* Component::createNewPeer (int styleFlags, void*) | |||
| return new AndroidComponentPeer (this, styleFlags); | |||
| } | |||
| jobject createOpenGLView (ComponentPeer* peer) | |||
| { | |||
| jobject parentView = static_cast <jobject> (peer->getNativeHandle()); | |||
| return getEnv()->CallObjectMethod (parentView, ComponentPeerView.createGLView); | |||
| } | |||
| //============================================================================== | |||
| bool Desktop::canUseSemiTransparentWindows() noexcept | |||
| @@ -23,22 +23,187 @@ | |||
| ============================================================================== | |||
| */ | |||
| // TODO | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (layout, "layout", "(IIII)V") \ | |||
| METHOD (invalidate, "invalidate", "(IIII)V") \ | |||
| DECLARE_JNI_CLASS (OpenGLView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$OpenGLView"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| jobject createOpenGLView (ComponentPeer*); | |||
| //============================================================================== | |||
| class AndroidGLContext : public OpenGLContext | |||
| { | |||
| public: | |||
| AndroidGLContext (OpenGLComponent* const component_, | |||
| ComponentPeer* peer, | |||
| const OpenGLPixelFormat& pixelFormat, | |||
| const AndroidGLContext* const sharedContext, | |||
| const bool isGLES2_) | |||
| : component (component_), | |||
| lastWidth (0), lastHeight (0), | |||
| isGLES2 (isGLES2_), | |||
| isInsideGLCallback (false) | |||
| { | |||
| getContextList().add (this); | |||
| jassert (peer != nullptr); | |||
| glView = GlobalRef (createOpenGLView (peer)); | |||
| } | |||
| ~AndroidGLContext() | |||
| { | |||
| properties.clear(); // to release any stored programs, etc that may be held in properties. | |||
| android.activity.callVoidMethod (JuceAppActivity.deleteView, glView.get()); | |||
| glView.clear(); | |||
| getContextList().removeValue (this); | |||
| } | |||
| //============================================================================== | |||
| bool makeActive() const noexcept { return isInsideGLCallback; } | |||
| bool makeInactive() const noexcept { return true; } | |||
| bool isActive() const noexcept { return isInsideGLCallback; } | |||
| void swapBuffers() {} | |||
| void* getRawContext() const noexcept { return 0; } | |||
| unsigned int getFrameBufferID() const { return 0; } | |||
| int getWidth() const { return lastWidth; } | |||
| int getHeight() const { return lastHeight; } | |||
| bool areShadersAvailable() const { return isGLES2; } | |||
| void updateWindowPosition (const Rectangle<int>& bounds) | |||
| { | |||
| if (lastWidth != bounds.getWidth() || lastHeight != bounds.getHeight()) | |||
| { | |||
| lastWidth = bounds.getWidth(); | |||
| lastHeight = bounds.getHeight(); | |||
| glView.callVoidMethod (OpenGLView.layout, | |||
| bounds.getX(), bounds.getY(), | |||
| lastWidth, lastHeight); | |||
| } | |||
| } | |||
| bool setSwapInterval (const int /*numFramesPerSwap*/) { return false; } | |||
| int getSwapInterval() const { return 0; } | |||
| //============================================================================== | |||
| void contextCreatedCallback() | |||
| { | |||
| isInsideGLCallback = true; | |||
| if (component != nullptr) | |||
| dynamic_cast <OpenGLComponent*> (component.get())->newOpenGLContextCreated(); | |||
| isInsideGLCallback = false; | |||
| } | |||
| void renderCallback() | |||
| { | |||
| isInsideGLCallback = true; | |||
| if (component != nullptr) | |||
| dynamic_cast <OpenGLComponent*> (component.get())->performRender(); | |||
| isInsideGLCallback = false; | |||
| } | |||
| //============================================================================== | |||
| static Array<AndroidGLContext*>& getContextList() | |||
| { | |||
| static Array <AndroidGLContext*> contexts; | |||
| return contexts; | |||
| } | |||
| static AndroidGLContext* findContextFor (JNIEnv* env, jobject glView) | |||
| { | |||
| const Array<AndroidGLContext*>& contexts = getContextList(); | |||
| for (int i = contexts.size(); --i >= 0;) | |||
| { | |||
| AndroidGLContext* const c = contexts.getUnchecked(i); | |||
| if (env->IsSameObject (c->glView.get(), glView)) | |||
| return c; | |||
| } | |||
| return nullptr; | |||
| } | |||
| static bool isAnyContextActive() | |||
| { | |||
| const Array<AndroidGLContext*>& contexts = getContextList(); | |||
| for (int i = contexts.size(); --i >= 0;) | |||
| { | |||
| const AndroidGLContext* const c = contexts.getUnchecked(i); | |||
| if (c->isInsideGLCallback) | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| GlobalRef glView; | |||
| private: | |||
| WeakReference<Component> component; | |||
| int lastWidth, lastHeight; | |||
| bool isGLES2; | |||
| bool isInsideGLCallback; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidGLContext); | |||
| }; | |||
| //============================================================================== | |||
| OpenGLContext* OpenGLComponent::createContext() | |||
| { | |||
| ComponentPeer* peer = getTopLevelComponent()->getPeer(); | |||
| if (peer != nullptr) | |||
| return new AndroidGLContext (this, peer, preferredPixelFormat, | |||
| dynamic_cast <const AndroidGLContext*> (contextToShareListsWith), | |||
| (flags & openGLES2) != 0); | |||
| return nullptr; | |||
| } | |||
| void* OpenGLComponent::getNativeWindowHandle() const | |||
| void OpenGLComponent::updateEmbeddedPosition (const Rectangle<int>& bounds) | |||
| { | |||
| return nullptr; | |||
| const ScopedLock sl (contextLock); | |||
| if (context != nullptr) | |||
| static_cast <AndroidGLContext*> (context.get())->updateWindowPosition (bounds); | |||
| } | |||
| bool OpenGLHelpers::isContextActive() | |||
| { | |||
| return AndroidGLContext::isAnyContextActive(); | |||
| } | |||
| void OpenGLComponent::updateEmbeddedPosition (const Rectangle<int>&) | |||
| //============================================================================== | |||
| JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024OpenGLView), contextCreated, void, (JNIEnv* env, jobject view)) | |||
| { | |||
| AndroidGLContext* const context = AndroidGLContext::findContextFor (env, view); | |||
| if (context != nullptr) | |||
| context->contextCreatedCallback(); | |||
| } | |||
| bool OpenGLHelpers::isContextActive() | |||
| JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024OpenGLView), render, void, (JNIEnv* env, jobject view)) | |||
| { | |||
| return false; | |||
| AndroidGLContext* const context = AndroidGLContext::findContextFor (env, view); | |||
| if (context != nullptr) | |||
| context->renderCallback(); | |||
| } | |||
| @@ -220,7 +220,11 @@ void OpenGLComponent::stopRenderThread() | |||
| //============================================================================== | |||
| OpenGLComponent::OpenGLComponent (const int flags_) | |||
| #if JUCE_ANDROID | |||
| : flags (flags_ & ~useBackgroundThread), | |||
| #else | |||
| : flags (flags_), | |||
| #endif | |||
| contextToShareListsWith (nullptr), | |||
| needToUpdateViewport (true), | |||
| needToDeleteContext (false), | |||
| @@ -35,6 +35,9 @@ | |||
| #endif | |||
| class OpenGLGraphicsContext; | |||
| #if JUCE_ANDROID | |||
| class AndroidGLContext; | |||
| #endif | |||
| //============================================================================== | |||
| /** | |||
| @@ -61,9 +64,9 @@ public: | |||
| */ | |||
| openGLDefault = 8, | |||
| #if JUCE_IOS | |||
| openGLES1 = 1, /**< On the iPhone, this selects openGL ES 1.0 */ | |||
| openGLES2 = 2, /**< On the iPhone, this selects openGL ES 2.0 */ | |||
| #if JUCE_IOS || JUCE_ANDROID | |||
| openGLES1 = 1, /**< This selects openGL ES 1.0 */ | |||
| openGLES2 = 2, /**< This selects openGL ES 2.0 */ | |||
| #endif | |||
| /** If this flag is enabled, the component will launch a background thread to | |||
| @@ -290,6 +293,10 @@ private: | |||
| int renderAndSwapBuffers(); // (This method has been deprecated) | |||
| #if JUCE_ANDROID | |||
| friend class AndroidGLContext; | |||
| #endif | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLComponent); | |||
| }; | |||