OSDN Git Service

Merge remote-tracking branch 'libgdxorigin/master' into lwpredesign
authorAriel Coppes <ariel.coppes@gemserk.com>
Sun, 24 Mar 2013 22:14:43 +0000 (19:14 -0300)
committerAriel Coppes <ariel.coppes@gemserk.com>
Sun, 24 Mar 2013 22:14:43 +0000 (19:14 -0300)
backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidGraphicsLiveWallpaper.java
backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidLiveWallpaper.java
backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidLiveWallpaperService.java
gdx/src/com/badlogic/gdx/android/AndroidWallpaperListener.java [new file with mode: 0644]
gdx/src/com/badlogic/gdx/graphics/Mesh.java
tests/gdx-tests-android/src/com/badlogic/gdx/tests/android/LiveWallpaper.java

index 134cdd7..ff1e519 100755 (executable)
@@ -2,6 +2,7 @@
  * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com)\r
  * \r
  * Modified by Elijah Cornell\r
+ * 2013.01 Modified by Jaroslaw Wisniewski <j.wisniewski@appsisle.com>\r
  * \r
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the\r
  * License. You may obtain a copy of the License at\r
@@ -15,6 +16,8 @@
 \r
 package com.badlogic.gdx.backends.android;\r
 \r
+import java.lang.reflect.Method;\r
+\r
 import javax.microedition.khronos.egl.EGL10;\r
 import javax.microedition.khronos.egl.EGLConfig;\r
 import javax.microedition.khronos.egl.EGLContext;\r
@@ -24,21 +27,16 @@ import android.content.Context;
 import android.opengl.GLSurfaceView;\r
 import android.opengl.GLSurfaceView.EGLConfigChooser;\r
 import android.opengl.GLSurfaceView.Renderer;\r
-import android.os.Build;\r
+import android.opengl.GLU;\r
 import android.util.DisplayMetrics;\r
 import android.view.Display;\r
+import android.view.SurfaceHolder;\r
 import android.view.View;\r
-import android.view.WindowManager;\r
 \r
 import com.badlogic.gdx.Gdx;\r
 import com.badlogic.gdx.Graphics;\r
-import com.badlogic.gdx.LifecycleListener;\r
-import com.badlogic.gdx.Graphics.BufferFormat;\r
-import com.badlogic.gdx.Graphics.DisplayMode;\r
-import com.badlogic.gdx.Graphics.GraphicsType;\r
-import com.badlogic.gdx.backends.android.surfaceview.DefaultGLSurfaceViewLW;\r
-import com.badlogic.gdx.backends.android.surfaceview.GLBaseSurfaceViewLW;\r
-import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20LW;\r
+import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20;\r
+import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceViewCupcake;\r
 import com.badlogic.gdx.backends.android.surfaceview.GdxEglConfigChooser;\r
 import com.badlogic.gdx.backends.android.surfaceview.ResolutionStrategy;\r
 import com.badlogic.gdx.graphics.GL10;\r
@@ -46,29 +44,29 @@ import com.badlogic.gdx.graphics.GL11;
 import com.badlogic.gdx.graphics.GL20;\r
 import com.badlogic.gdx.graphics.GLCommon;\r
 import com.badlogic.gdx.graphics.Mesh;\r
-import com.badlogic.gdx.graphics.Pixmap;\r
 import com.badlogic.gdx.graphics.Texture;\r
 import com.badlogic.gdx.graphics.glutils.FrameBuffer;\r
 import com.badlogic.gdx.graphics.glutils.ShaderProgram;\r
 import com.badlogic.gdx.math.WindowedMean;\r
-import com.badlogic.gdx.utils.Array;\r
 \r
 /** An implementation of {@link Graphics} for Android.\r
  * \r
  * @author mzechner */\r
 public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {\r
-\r
-       final GLBaseSurfaceViewLW view;\r
-\r
-       AndroidLiveWallpaper app;\r
-\r
+       \r
+       // jw: changed\r
+       //final GLBaseSurfaceViewLW view;\r
+       final View view;\r
+       \r
        int width;\r
        int height;\r
-\r
+       AndroidLiveWallpaper app;\r
+       \r
        protected GLCommon gl;\r
        protected GL10 gl10;\r
        protected GL11 gl11;\r
        protected GL20 gl20;\r
+       protected GLU glu;\r
        protected EGLContext eglContext;\r
        protected String extensions;\r
 \r
@@ -90,263 +88,168 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
        protected float ppcX = 0;\r
        protected float ppcY = 0;\r
        protected float density = 1;\r
-       \r
+\r
        private final AndroidApplicationConfiguration config;\r
        private BufferFormat bufferFormat = new BufferFormat(5, 6, 5, 0, 16, 0, 0, false);\r
        protected boolean isContinuous = true;\r
 \r
-       protected boolean checkGL20 () {\r
-               EGL10 egl = (EGL10)EGLContext.getEGL();\r
-               EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);\r
-\r
-               int[] version = new int[2];\r
-               egl.eglInitialize(display, version);\r
-\r
-               int EGL_OPENGL_ES2_BIT = 4;\r
-               int[] configAttribs = {EGL10.EGL_RED_SIZE, 4, EGL10.EGL_GREEN_SIZE, 4, EGL10.EGL_BLUE_SIZE, 4, EGL10.EGL_RENDERABLE_TYPE,\r
-                       EGL_OPENGL_ES2_BIT, EGL10.EGL_NONE};\r
-\r
-               EGLConfig[] configs = new EGLConfig[10];\r
-               int[] num_config = new int[1];\r
-               egl.eglChooseConfig(display, configAttribs, configs, 10, num_config);\r
-               egl.eglTerminate(display);\r
-               return num_config[0] > 0;\r
-       }\r
-\r
-       @Override\r
-       public GL10 getGL10 () {\r
-               return gl10;\r
-       }\r
-\r
-       @Override\r
-       public GL11 getGL11 () {\r
-               return gl11;\r
-       }\r
-\r
-       @Override\r
-       public GL20 getGL20 () {\r
-               return gl20;\r
-       }\r
-\r
-       @Override\r
-       public int getHeight () {\r
-               return height;\r
-       }\r
-\r
-       @Override\r
-       public int getWidth () {\r
-               return width;\r
-       }\r
-\r
-       @Override\r
-       public boolean isGL11Available () {\r
-               return gl11 != null;\r
-       }\r
-\r
-       @Override\r
-       public boolean isGL20Available () {\r
-               return gl20 != null;\r
-       }\r
-\r
-       protected void logConfig (EGLConfig config) {\r
-               EGL10 egl = (EGL10)EGLContext.getEGL();\r
-               EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);\r
-               int r = getAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);\r
-               int g = getAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);\r
-               int b = getAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);\r
-               int a = getAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);\r
-               int d = getAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);\r
-               int s = getAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);\r
-               int samples = Math.max(getAttrib(egl, display, config, EGL10.EGL_SAMPLES, 0),\r
-                       getAttrib(egl, display, config, GdxEglConfigChooser.EGL_COVERAGE_SAMPLES_NV, 0));\r
-               boolean coverageSample = getAttrib(egl, display, config, GdxEglConfigChooser.EGL_COVERAGE_SAMPLES_NV, 0) != 0;\r
-\r
-               Gdx.app.log("AndroidGraphics", "framebuffer: (" + r + ", " + g + ", " + b + ", " + a + ")");\r
-               Gdx.app.log("AndroidGraphics", "depthbuffer: (" + d + ")");\r
-               Gdx.app.log("AndroidGraphics", "stencilbuffer: (" + s + ")");\r
-               Gdx.app.log("AndroidGraphics", "samples: (" + samples + ")");\r
-               Gdx.app.log("AndroidGraphics", "coverage sampling: (" + coverageSample + ")");\r
-\r
-               bufferFormat = new BufferFormat(r, g, b, a, d, s, samples, coverageSample);\r
+       \r
+       public AndroidGraphicsLiveWallpaper (AndroidLiveWallpaper app, AndroidApplicationConfiguration config, ResolutionStrategy resolutionStrategy) {\r
+               this.config = config;\r
+               this.app = app;\r
+               view = createGLSurfaceView(app.service, config.useGL20, resolutionStrategy);\r
+               setPreserveContext(view);\r
        }\r
-\r
-       private int getAttrib (EGL10 egl, EGLDisplay display, EGLConfig config, int attrib, int defValue) {\r
-               if (egl.eglGetConfigAttrib(display, config, attrib, value)) {\r
-                       return value[0];\r
+       \r
+       // jw: it will be called only after current GLSurfaceViewLW family of methods \r
+       // will be replaced by subclass of original GLSurfaceView, i'm working on it:)\r
+       // <- ok it is in use now\r
+       private void setPreserveContext (Object view) {\r
+               int sdkVersion = Integer.parseInt(android.os.Build.VERSION.SDK);\r
+               if (sdkVersion >= 11 && view instanceof GLSurfaceView) {\r
+                       try {\r
+                               Method method = null;\r
+                               for (Method m : view.getClass().getMethods()) {\r
+                                       if (m.getName().equals("setPreserveEGLContextOnPause")) {\r
+                                               method = m;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if (method != null) {\r
+                                       method.invoke((GLSurfaceView)view, true);\r
+                               }\r
+                       } catch (Exception e) {\r
+                       }\r
                }\r
-               return defValue;\r
        }\r
 \r
-       void resume () {\r
-               synchronized (synch) {\r
-                       running = true;\r
-                       resume = true;\r
+       \r
+       // jw: I replaced GL..SurfaceViewLW classes with them original counterparts, if it will work \r
+       // on known devices, on opengl 1.0 and 2.0, and all possible SDK versions.. You can remove \r
+       // GL..SurfaceViewLW family of classes completely (there is no use for them).\r
+       \r
+       // -> specific for live wallpapers\r
+       // jw: synchronized access to current wallpaper surface holder\r
+       SurfaceHolder getSurfaceHolder() {\r
+               synchronized (app.service.sync) {\r
+                       return app.service.getSurfaceHolder();\r
                }\r
        }\r
+       // <- specific for live wallpapers\r
+       \r
+       // Grabbed from original AndroidGraphics class, with modifications:\r
+       //      + overrided getHolder in created GLSurfaceView instances\r
+       // + Activity changed to Context (as it should be in AndroidGraphics I think;p)\r
+       private View createGLSurfaceView (Context context, boolean useGL2, final ResolutionStrategy resolutionStrategy) {\r
+               EGLConfigChooser configChooser = getEglConfigChooser();\r
 \r
-       void pause () {\r
-               synchronized (synch) {\r
-                       if (!running) return;\r
-                       running = false;\r
-                       pause = true;\r
-                       while (pause) {\r
-                               try {\r
-                                       synch.wait();\r
-                               } catch (InterruptedException ignored) {\r
-                                       Gdx.app.log("AndroidGraphics", "waiting for pause synchronization failed!");\r
+               if (useGL2 && checkGL20()) {\r
+                       GLSurfaceView20 view = new GLSurfaceView20(context, resolutionStrategy) {\r
+                               // -> specific for live wallpapers\r
+                               @Override\r
+                               public SurfaceHolder getHolder () {\r
+                                       return getSurfaceHolder();\r
                                }\r
-                       }\r
-               }\r
-       }\r
-\r
-       void destroy () {\r
-               synchronized (synch) {\r
-                       running = false;\r
-                       destroy = true;\r
-\r
-                       while (destroy) {\r
-                               try {\r
-                                       synch.wait();\r
-                               } catch (InterruptedException ex) {\r
-                                       Gdx.app.log("AndroidGraphics", "waiting for destroy synchronization failed!");\r
+                               public void onDestroy () {\r
+                                       onDetachedFromWindow(); // calls GLSurfaceView.mGLThread.requestExitAndWait();\r
                                }\r
+                               // <- specific for live wallpapers\r
+                       };\r
+                       \r
+                       if (configChooser != null)\r
+                               view.setEGLConfigChooser(configChooser);\r
+                       else\r
+                               view.setEGLConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil);\r
+                       view.setRenderer(this);\r
+                       return view;\r
+               } else {\r
+                       config.useGL20 = false;\r
+                       configChooser = getEglConfigChooser();\r
+                       int sdkVersion = Integer.parseInt(android.os.Build.VERSION.SDK);\r
+\r
+                       if (sdkVersion >= 11) {\r
+                               GLSurfaceView view = new GLSurfaceView(context) {\r
+                                       @Override\r
+                                       protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {\r
+                                               ResolutionStrategy.MeasuredDimension measures = resolutionStrategy.calcMeasures(widthMeasureSpec,\r
+                                                       heightMeasureSpec);\r
+                                               setMeasuredDimension(measures.width, measures.height);\r
+                                       }\r
+                                       // -> specific for live wallpapers\r
+                                       @Override\r
+                                       public SurfaceHolder getHolder () {\r
+                                               return getSurfaceHolder();\r
+                                       }\r
+                                       public void onDestroy () {\r
+                                               onDetachedFromWindow(); // calls GLSurfaceView.mGLThread.requestExitAndWait();\r
+                                       }\r
+                                       // <- specific for live wallpapers\r
+                               };\r
+                               if (configChooser != null)\r
+                                       view.setEGLConfigChooser(configChooser);\r
+                               else\r
+                                       view.setEGLConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil);\r
+                               view.setRenderer(this);\r
+                               return view;\r
+                       } else {\r
+                               GLSurfaceViewCupcake view = new GLSurfaceViewCupcake(context, resolutionStrategy) {\r
+                                       // -> specific for live wallpapers\r
+                                       @Override\r
+                                       public SurfaceHolder getHolder () {\r
+                                               return getSurfaceHolder();\r
+                                       }\r
+                                       // <- specific for live wallpapers\r
+                               };\r
+                               if (configChooser != null)\r
+                                       view.setEGLConfigChooser(configChooser);\r
+                               else\r
+                                       view.setEGLConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil);\r
+                               view.setRenderer(this);\r
+                               return view;\r
                        }\r
                }\r
        }\r
-\r
-       /** {@inheritDoc} */\r
-       @Override\r
-       public float getDeltaTime () {\r
-               return mean.getMean() == 0 ? deltaTime : mean.getMean();\r
-       }\r
-\r
-       @Override\r
-       public float getRawDeltaTime () {\r
-               return deltaTime;\r
-       }\r
-\r
-       /** {@inheritDoc} */\r
-       @Override\r
-       public GraphicsType getType () {\r
-               return GraphicsType.AndroidGL;\r
-       }\r
-\r
-       /** {@inheritDoc} */\r
-       @Override\r
-       public int getFramesPerSecond () {\r
-               return fps;\r
-       }\r
-\r
-       /** {@inheritDoc} */\r
-       @Override\r
-       public GLCommon getGLCommon () {\r
-               return gl;\r
-       }\r
-\r
-       @Override\r
-       public float getPpiX () {\r
-               return ppiX;\r
-       }\r
-\r
-       @Override\r
-       public float getPpiY () {\r
-               return ppiY;\r
-       }\r
-\r
-       @Override\r
-       public float getPpcX () {\r
-               return ppcX;\r
-       }\r
-\r
-       @Override\r
-       public float getPpcY () {\r
-               return ppcY;\r
-       }\r
-\r
-       @Override\r
-       public boolean supportsDisplayModeChange () {\r
-               return false;\r
-       }\r
-\r
-       @Override\r
-       public boolean setDisplayMode (DisplayMode displayMode) {\r
-               return false;\r
-       }\r
-\r
-       @Override\r
-       public void setTitle (String title) {\r
-\r
-       }\r
-\r
-       class AndroidDisplayMode extends DisplayMode {\r
-               protected AndroidDisplayMode (int width, int height, int refreshRate, int bitsPerPixel) {\r
-                       super(width, height, refreshRate, bitsPerPixel);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public BufferFormat getBufferFormat () {\r
-               return bufferFormat;\r
-       }\r
-\r
-       @Override\r
-       public void setVSync (boolean vsync) {\r
-       }\r
-\r
-       @Override\r
-       public boolean supportsExtension (String extension) {\r
-               if (extensions == null) extensions = Gdx.gl.glGetString(GL10.GL_EXTENSIONS);\r
-               return extensions.contains(extension);\r
-       }\r
-\r
-       public boolean isContinuousRendering () {\r
-               return isContinuous;\r
-       }\r
-\r
-       @Override\r
-       public boolean isFullscreen () {\r
-               return true;\r
-       }\r
-\r
-       public AndroidGraphicsLiveWallpaper (AndroidLiveWallpaper app, AndroidApplicationConfiguration config, ResolutionStrategy resolutionStrategy) {\r
-               this.config = config;\r
-               view = createGLSurfaceView(app, config.useGL20, resolutionStrategy);\r
-               this.app = app;\r
-\r
-       }\r
-\r
+       \r
+       \r
+       // jw: old implementation, makes use of GL..SurfaceViewLW\r
+       /*\r
        private GLBaseSurfaceViewLW createGLSurfaceView (AndroidLiveWallpaper app, boolean useGL2,\r
                ResolutionStrategy resolutionStrategy) {\r
+\r
+               // jw: synchronized with original AndroidGraphics \r
                EGLConfigChooser configChooser = getEglConfigChooser();\r
 \r
                if (useGL2 && checkGL20()) {\r
-                       GLSurfaceView20LW view = new GLSurfaceView20LW(app.getEngine(), resolutionStrategy);\r
-                       if (configChooser != null) \r
+                       GLSurfaceView20LW view = new GLSurfaceView20LW(app.getService(), resolutionStrategy);\r
+                       if (configChooser != null)\r
                                view.setEGLConfigChooser(configChooser);\r
                        else\r
                                view.setEGLConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil);\r
                        view.setRenderer(this);\r
                        return view;\r
                } else {\r
-                       //GL1\r
                        config.useGL20 = false;\r
                        configChooser = getEglConfigChooser();\r
 \r
-                       GLBaseSurfaceViewLW view = new DefaultGLSurfaceViewLW(app.getEngine(), resolutionStrategy);\r
-                       if (configChooser != null) \r
+                       GLBaseSurfaceViewLW view = new DefaultGLSurfaceViewLW(app.getService(), resolutionStrategy);\r
+                       if (configChooser != null)\r
                                view.setEGLConfigChooser(configChooser);\r
                        else\r
                                view.setEGLConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil);\r
-\r
                        view.setRenderer(this);\r
                        return view;\r
                }\r
-       }\r
-\r
+       }*/\r
+       \r
+       \r
+       // jw: changed, method replaced with implementation from original AndroidGraphics\r
        private EGLConfigChooser getEglConfigChooser () {\r
-       return new GdxEglConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil, config.numSamples,\r
+               return new GdxEglConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil, config.numSamples,\r
                        config.useGL20);\r
+       }\r
+       \r
        /*\r
+       private EGLConfigChooser getEglConfigChooser () {\r
                if (!Build.DEVICE.equalsIgnoreCase("GT-I7500"))\r
                        return null;\r
                else\r
@@ -363,14 +266,16 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
                                        egl.eglChooseConfig(display, attributes, configs, 1, result);\r
                                        return configs[0];\r
                                }\r
-                       };*/\r
-       }\r
+                       };\r
+       }*/\r
 \r
        private void updatePpi () {\r
                DisplayMetrics metrics = new DisplayMetrics();\r
 \r
-               final Display display = ((WindowManager)app.getService().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();\r
-               display.getMetrics(metrics);\r
+               // jw: changed\r
+               app.getWindowManager().getDefaultDisplay().getMetrics(metrics);\r
+               //final Display display = ((WindowManager)app.getService().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();\r
+               //display.getMetrics(metrics);\r
 \r
                ppiX = metrics.xdpi;\r
                ppiY = metrics.ydpi;\r
@@ -378,11 +283,71 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
                ppcY = metrics.ydpi / 2.54f;\r
                density = metrics.density;\r
        }\r
+       \r
+       protected boolean checkGL20 () {\r
+               EGL10 egl = (EGL10)EGLContext.getEGL();\r
+               EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);\r
+\r
+               int[] version = new int[2];\r
+               egl.eglInitialize(display, version);\r
+\r
+               int EGL_OPENGL_ES2_BIT = 4;\r
+               int[] configAttribs = {EGL10.EGL_RED_SIZE, 4, EGL10.EGL_GREEN_SIZE, 4, EGL10.EGL_BLUE_SIZE, 4, EGL10.EGL_RENDERABLE_TYPE,\r
+                       EGL_OPENGL_ES2_BIT, EGL10.EGL_NONE};\r
+\r
+               EGLConfig[] configs = new EGLConfig[10];\r
+               int[] num_config = new int[1];\r
+               egl.eglChooseConfig(display, configAttribs, configs, 10, num_config);\r
+               egl.eglTerminate(display);\r
+               return num_config[0] > 0;\r
+       }\r
+\r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public GL10 getGL10 () {\r
+               return gl10;\r
+       }\r
 \r
-       // private static boolean isPowerOfTwo(int value) {\r
-       // return ((value != 0) && (value & (value - 1)) == 0);\r
-       // }\r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public GL11 getGL11 () {\r
+               return gl11;\r
+       }\r
 \r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public GL20 getGL20 () {\r
+               return gl20;\r
+       }\r
+\r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public int getHeight () {\r
+               return height;\r
+       }\r
+\r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public int getWidth () {\r
+               return width;\r
+       }\r
+\r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public boolean isGL11Available () {\r
+               return gl11 != null;\r
+       }\r
+\r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public boolean isGL20Available () {\r
+               return gl20 != null;\r
+       }\r
+       \r
+       private static boolean isPowerOfTwo(int value) {\r
+               return ((value != 0) && (value & (value - 1)) == 0);\r
+       }\r
+       \r
        /** This instantiates the GL10, GL11 and GL20 instances. Includes the check for certain devices that pretend to support GL11 but\r
         * fuck up vertex buffer objects. This includes the pixelflinger which segfaults when buffers are deleted as well as the\r
         * Motorola CLIQ and the Samsung Behold II.\r
@@ -391,10 +356,13 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
        private void setupGL (javax.microedition.khronos.opengles.GL10 gl) {\r
                if (gl10 != null || gl20 != null) return;\r
 \r
-               boolean isGL20 = checkGL20();\r
-               Gdx.app.log("AndroidGraphics", "GL20: " + isGL20);\r
+               // jw: disabled\r
+               //boolean isGL20 = checkGL20();\r
+               //Gdx.app.log("AndroidGraphics", "GL20: " + isGL20);\r
 \r
-               if (view instanceof GLSurfaceView20LW) {\r
+               // jw: changed\r
+               //if (view instanceof GLSurfaceView20LW) {\r
+               if (view instanceof GLSurfaceView20) {\r
                        gl20 = new AndroidGL20();\r
                        this.gl = gl20;\r
                } else {\r
@@ -402,7 +370,7 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
                        this.gl = gl10;\r
                        if (gl instanceof javax.microedition.khronos.opengles.GL11) {\r
                                String renderer = gl.glGetString(GL10.GL_RENDERER);\r
-                               if(renderer != null) {\r
+                               if (renderer != null) { // silly GT-I7500\r
                                        if (!renderer.toLowerCase().contains("pixelflinger")\r
                                                && !(android.os.Build.MODEL.equals("MB200") || android.os.Build.MODEL.equals("MB220") || android.os.Build.MODEL\r
                                                        .contains("Behold"))) {\r
@@ -418,10 +386,11 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
                Gdx.gl11 = gl11;\r
                Gdx.gl20 = gl20;\r
 \r
-               Gdx.app.log("AndroidGraphics", "OGL renderer: " + gl.glGetString(GL10.GL_RENDERER));\r
-               Gdx.app.log("AndroidGraphics", "OGL vendor: " + gl.glGetString(GL10.GL_VENDOR));\r
-               Gdx.app.log("AndroidGraphics", "OGL version: " + gl.glGetString(GL10.GL_VERSION));\r
-               Gdx.app.log("AndroidGraphics", "OGL extensions: " + gl.glGetString(GL10.GL_EXTENSIONS));\r
+               // moved to logConfig\r
+               //Gdx.app.log("AndroidGraphics", "OGL renderer: " + gl.glGetString(GL10.GL_RENDERER));\r
+               //Gdx.app.log("AndroidGraphics", "OGL vendor: " + gl.glGetString(GL10.GL_VENDOR));\r
+               //Gdx.app.log("AndroidGraphics", "OGL version: " + gl.glGetString(GL10.GL_VERSION));\r
+               //Gdx.app.log("AndroidGraphics", "OGL extensions: " + gl.glGetString(GL10.GL_EXTENSIONS));\r
        }\r
 \r
        @Override\r
@@ -430,11 +399,21 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
                this.height = height;\r
                updatePpi();\r
                gl.glViewport(0, 0, this.width, this.height);\r
-               app.getListener().resize(width, height);\r
+               \r
+               // jw: moved from onSurfaceCreated (as in AndroidGraphics class)\r
+               if (created == false) {\r
+                       app.listener.create();\r
+                       created = true;\r
+                       synchronized (this) {\r
+                               running = true;\r
+                       }\r
+               }\r
+               app.listener.resize(width, height);\r
        }\r
 \r
        @Override\r
        public void onSurfaceCreated (javax.microedition.khronos.opengles.GL10 gl, EGLConfig config) {\r
+               eglContext = ((EGL10)EGLContext.getEGL()).eglGetCurrentContext();       // jw: added\r
                setupGL(gl);\r
                logConfig(config);\r
                updatePpi();\r
@@ -444,8 +423,14 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
                ShaderProgram.invalidateAllShaderPrograms(app);\r
                FrameBuffer.invalidateAllFrameBuffers(app);\r
 \r
-               final Display display = ((WindowManager)app.getService().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();\r
-\r
+               if (AndroidLiveWallpaperService.DEBUG) {        // to prevent creating too many string buffers in live wallpapers\r
+                       Gdx.app.debug("AndroidGraphics", Mesh.getManagedStatus());\r
+                       Gdx.app.debug("AndroidGraphics", Texture.getManagedStatus());\r
+                       Gdx.app.debug("AndroidGraphics", ShaderProgram.getManagedStatus());\r
+                       Gdx.app.debug("AndroidGraphics", FrameBuffer.getManagedStatus());\r
+               }\r
+               \r
+               Display display = app.getWindowManager().getDefaultDisplay();\r
                this.width = display.getWidth();\r
                this.height = display.getHeight();\r
                mean = new WindowedMean(5);\r
@@ -453,41 +438,124 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
 \r
                gl.glViewport(0, 0, this.width, this.height);\r
 \r
-               if (created == false) {\r
+               // jw: moved to onSurfaceChanged (as in AndroidGraphics class)\r
+               /*if (created == false) {\r
                        app.getListener().create();\r
                        created = true;\r
                        synchronized (this) {\r
                                running = true;\r
                        }\r
+               }*/\r
+       }\r
+\r
+       private void logConfig (EGLConfig config) {\r
+               EGL10 egl = (EGL10)EGLContext.getEGL();\r
+               EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);\r
+               int r = getAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);\r
+               int g = getAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);\r
+               int b = getAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);\r
+               int a = getAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);\r
+               int d = getAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);\r
+               int s = getAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);\r
+               int samples = Math.max(getAttrib(egl, display, config, EGL10.EGL_SAMPLES, 0),\r
+                       getAttrib(egl, display, config, GdxEglConfigChooser.EGL_COVERAGE_SAMPLES_NV, 0));\r
+               boolean coverageSample = getAttrib(egl, display, config, GdxEglConfigChooser.EGL_COVERAGE_SAMPLES_NV, 0) != 0;\r
+\r
+               // print configuration just one time (on some devices gl context is recreated every time when device is locked / unlocked - every time when screen turns on and off)\r
+               if (!configLogged)\r
+               {\r
+                       \r
+                       if (gl != null)\r
+                       {\r
+                               Gdx.app.log("AndroidGraphics", "OGL renderer: " + gl.glGetString(GL10.GL_RENDERER));\r
+                               Gdx.app.log("AndroidGraphics", "OGL vendor: " + gl.glGetString(GL10.GL_VENDOR));\r
+                               Gdx.app.log("AndroidGraphics", "OGL version: " + gl.glGetString(GL10.GL_VERSION));\r
+                               Gdx.app.log("AndroidGraphics", "OGL extensions: " + gl.glGetString(GL10.GL_EXTENSIONS));\r
+                               configLogged = true;\r
+                       }\r
+                       \r
+                       Gdx.app.log("AndroidGraphics", "framebuffer: (" + r + ", " + g + ", " + b + ", " + a + ")");\r
+                       Gdx.app.log("AndroidGraphics", "depthbuffer: (" + d + ")");\r
+                       Gdx.app.log("AndroidGraphics", "stencilbuffer: (" + s + ")");\r
+                       Gdx.app.log("AndroidGraphics", "samples: (" + samples + ")");\r
+                       Gdx.app.log("AndroidGraphics", "coverage sampling: (" + coverageSample + ")");\r
                }\r
+               \r
+               bufferFormat = new BufferFormat(r, g, b, a, d, s, samples, coverageSample);\r
        }\r
 \r
+       boolean configLogged = false;\r
+       \r
        int[] value = new int[1];\r
 \r
+       private int getAttrib (EGL10 egl, EGLDisplay display, EGLConfig config, int attrib, int defValue) {\r
+               if (egl.eglGetConfigAttrib(display, config, attrib, value)) {\r
+                       return value[0];\r
+               }\r
+               return defValue;\r
+       }\r
+\r
        Object synch = new Object();\r
 \r
-// public void destroy() {\r
-// synchronized (synch) {\r
-// running = false;\r
-// destroy = true;\r
-//\r
-// // TODO: Why was the wait here? Causes deadlock!?!\r
-// // while (destroy) {\r
-// // try {\r
-// // synch.wait();\r
-// // } catch (InterruptedException ex) {\r
-// // }\r
-// // }\r
-// }\r
-// }\r
+       void resume () {\r
+               synchronized (synch) {\r
+                       running = true;\r
+                       resume = true;\r
+                       \r
+                       // by jw: added synchronization, there was nothing before\r
+                       while (resume) {\r
+                               try {\r
+                                       synch.wait();\r
+                               } catch (InterruptedException ignored) {\r
+                                       Gdx.app.log("AndroidGraphics", "waiting for resume synchronization failed!");\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       // jw: never called on lvp, why? see description in AndroidLiveWallpaper.onPause\r
+       void pause () {\r
+               synchronized (synch) {\r
+                       if (!running) return;\r
+                       running = false;\r
+                       pause = true;\r
+                       \r
+                       while (pause) {\r
+                               try {\r
+                                       synch.wait();\r
+                               } catch (InterruptedException ignored) {\r
+                                       Gdx.app.log("AndroidGraphics", "waiting for pause synchronization failed!");\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       // jw: never called on lvp\r
+       void destroy () {\r
+               synchronized (synch) {\r
+                       running = false;\r
+                       destroy = true;\r
+\r
+                       while (destroy) {\r
+                               try {\r
+                                       synch.wait();\r
+                               } catch (InterruptedException ex) {\r
+                                       Gdx.app.log("AndroidGraphics", "waiting for destroy synchronization failed!");\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
 \r
        @Override\r
        public void onDrawFrame (javax.microedition.khronos.opengles.GL10 gl) {\r
-\r
                long time = System.nanoTime();\r
                deltaTime = (time - lastFrameTime) / 1000000000.0f;\r
                lastFrameTime = time;\r
-               mean.addValue(deltaTime);\r
+               \r
+               // jw: after pause deltaTime can have somewhat huge value and it destabilize mean, so I propose to just cut it of\r
+               mean.addValue(resume ? 0.0f : deltaTime);\r
+               //mean.addValue(deltaTime);\r
 \r
                boolean lrunning = false;\r
                boolean lpause = false;\r
@@ -502,6 +570,8 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
 \r
                        if (resume) {\r
                                resume = false;\r
+                               // by jw: originally was not synchronized\r
+                               synch.notifyAll();\r
                        }\r
 \r
                        if (pause) {\r
@@ -516,52 +586,56 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
                }\r
 \r
                if (lresume) {\r
-                       Array<LifecycleListener> listeners = app.lifecycleListeners;\r
-                       synchronized(listeners) {\r
-                               for(LifecycleListener listener: listeners) {\r
-                                       listener.resume();\r
-                               }\r
-                       }\r
-                       app.getListener().resume();\r
+                       //((AndroidAudio)app.getAudio()).resume();      // jw: moved to AndroidLiveWallpaper.onResume\r
+                       app.listener.resume();\r
                        Gdx.app.log("AndroidGraphics", "resumed");\r
                }\r
 \r
                // HACK: added null check to handle set wallpaper from preview null\r
                // error in renderer\r
+               // jw: this hack is not working always, renderer ends with error for some devices - because of uninitialized gl context\r
+               // jw: now it shouldn't be necessary - after wallpaper backend refactoring:)\r
                if (lrunning && (Gdx.graphics.getGL10() != null || Gdx.graphics.getGL11() != null || Gdx.graphics.getGL20() != null)) {\r
 \r
+                       // jw: changed\r
+                       synchronized (app.runnables) {\r
+                               app.executedRunnables.clear();\r
+                               app.executedRunnables.addAll(app.runnables);\r
+                               app.runnables.clear();\r
+\r
+                               for (int i = 0; i < app.executedRunnables.size; i++) {\r
+                                       try {\r
+                                               app.executedRunnables.get(i).run();\r
+                                       } catch (Throwable t) {\r
+                                               t.printStackTrace();\r
+                                       }\r
+                               }\r
+                       }\r
+                       /*\r
                        synchronized (app.runnables) {\r
                                for (int i = 0; i < app.runnables.size; i++) {\r
                                        app.runnables.get(i).run();\r
                                }\r
                                app.runnables.clear();\r
                        }\r
+                       */\r
+                       \r
 \r
                        app.input.processEvents();\r
-                       app.getListener().render();\r
+                       app.listener.render();\r
                }\r
 \r
+               // jw: never called on lvp, why? see description in AndroidLiveWallpaper.onPause\r
                if (lpause) {\r
-                       Array<LifecycleListener> listeners = app.lifecycleListeners;\r
-                       synchronized(listeners) {\r
-                               for(LifecycleListener listener: listeners) {\r
-                                       listener.pause();\r
-                               }\r
-                       }\r
-                       app.getListener().pause();\r
-                       ((AndroidAudio)app.getAudio()).pause();\r
+                       app.listener.pause();\r
+                       //((AndroidAudio)app.getAudio()).pause();               jw: moved to AndroidLiveWallpaper.onPause\r
                        Gdx.app.log("AndroidGraphics", "paused");\r
                }\r
 \r
+               // jw: never called on lwp, why? see description in AndroidLiveWallpaper.onPause\r
                if (ldestroy) {\r
-                       Array<LifecycleListener> listeners = app.lifecycleListeners;\r
-                       synchronized(listeners) {\r
-                               for(LifecycleListener listener: listeners) {\r
-                                       listener.dispose();\r
-                               }\r
-                       }\r
-                       app.getListener().dispose();\r
-                       ((AndroidAudio)app.getAudio()).dispose();\r
+                       app.listener.dispose();\r
+                       //((AndroidAudio)app.getAudio()).dispose();      jw: moved to AndroidLiveWallpaper.onDestroy\r
                        Gdx.app.log("AndroidGraphics", "destroyed");\r
                }\r
 \r
@@ -573,25 +647,73 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
                frames++;\r
        }\r
 \r
-       protected void clearManagedCaches () {\r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public float getDeltaTime () {\r
+               return mean.getMean() == 0 ? deltaTime : mean.getMean();\r
+       }\r
+\r
+       @Override\r
+       public float getRawDeltaTime () {\r
+               return deltaTime;\r
+       }\r
+\r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public GraphicsType getType () {\r
+               return GraphicsType.AndroidGL;\r
+       }\r
+\r
+       /** {@inheritDoc} */\r
+       @Override\r
+       public int getFramesPerSecond () {\r
+               return fps;\r
+       }\r
+       \r
+       public void clearManagedCaches () {\r
                Mesh.clearAllMeshes(app);\r
                Texture.clearAllTextures(app);\r
                ShaderProgram.clearAllShaderPrograms(app);\r
                FrameBuffer.clearAllFrameBuffers(app);\r
 \r
-               Gdx.app.log("AndroidGraphics", Mesh.getManagedStatus());\r
-               Gdx.app.log("AndroidGraphics", Texture.getManagedStatus());\r
-               Gdx.app.log("AndroidGraphics", ShaderProgram.getManagedStatus());\r
-               Gdx.app.log("AndroidGraphics", FrameBuffer.getManagedStatus());\r
+               if (AndroidLiveWallpaperService.DEBUG) {        // to prevent creating too many string buffers in live wallpapers\r
+                       Gdx.app.debug("AndroidGraphics", Mesh.getManagedStatus());\r
+                       Gdx.app.debug("AndroidGraphics", Texture.getManagedStatus());\r
+                       Gdx.app.debug("AndroidGraphics", ShaderProgram.getManagedStatus());\r
+                       Gdx.app.debug("AndroidGraphics", FrameBuffer.getManagedStatus());\r
+               }\r
        }\r
 \r
-       public GLBaseSurfaceViewLW getView () {\r
+       // jw: changed this\r
+       //public GLBaseSurfaceViewLW getView () {\r
+       public View getView () {\r
                return view;\r
        }\r
 \r
+       /** {@inheritDoc} */\r
        @Override\r
-       public DisplayMode[] getDisplayModes () {\r
-               return new DisplayMode[0];\r
+       public GLCommon getGLCommon () {\r
+               return gl;\r
+       }\r
+\r
+       @Override\r
+       public float getPpiX () {\r
+               return ppiX;\r
+       }\r
+\r
+       @Override\r
+       public float getPpiY () {\r
+               return ppiY;\r
+       }\r
+\r
+       @Override\r
+       public float getPpcX () {\r
+               return ppcX;\r
+       }\r
+\r
+       @Override\r
+       public float getPpcY () {\r
+               return ppcY;\r
        }\r
 \r
        @Override\r
@@ -600,13 +722,18 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
        }\r
 \r
        @Override\r
-       public DisplayMode getDesktopDisplayMode () {\r
-               DisplayMetrics metrics = new DisplayMetrics();\r
+       public boolean supportsDisplayModeChange () {\r
+               return false;\r
+       }\r
 \r
-               final Display display = ((WindowManager)app.getService().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();\r
-               display.getMetrics(metrics);\r
+       @Override\r
+       public boolean setDisplayMode (DisplayMode displayMode) {\r
+               return false;\r
+       }\r
 \r
-               return new AndroidDisplayMode(metrics.widthPixels, metrics.heightPixels, 0, 0);\r
+       @Override\r
+       public DisplayMode[] getDisplayModes () {\r
+               return new DisplayMode[] {getDesktopDisplayMode()};\r
        }\r
 \r
        @Override\r
@@ -615,19 +742,69 @@ public final class AndroidGraphicsLiveWallpaper implements Graphics, Renderer {
        }\r
 \r
        @Override\r
+       public void setTitle (String title) {\r
+\r
+       }\r
+\r
+       private class AndroidDisplayMode extends DisplayMode {\r
+               protected AndroidDisplayMode (int width, int height, int refreshRate, int bitsPerPixel) {\r
+                       super(width, height, refreshRate, bitsPerPixel);\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public DisplayMode getDesktopDisplayMode () {\r
+               DisplayMetrics metrics = new DisplayMetrics();\r
+               app.getWindowManager().getDefaultDisplay().getMetrics(metrics);\r
+               return new AndroidDisplayMode(metrics.widthPixels, metrics.heightPixels, 0, 0);\r
+       }\r
+\r
+       @Override\r
+       public BufferFormat getBufferFormat () {\r
+               return bufferFormat;\r
+       }\r
+\r
+       @Override\r
+       public void setVSync (boolean vsync) {\r
+       }\r
+\r
+       @Override\r
+       public boolean supportsExtension (String extension) {\r
+               if (extensions == null) extensions = Gdx.gl.glGetString(GL10.GL_EXTENSIONS);\r
+               return extensions.contains(extension);\r
+       }\r
+\r
+       @Override\r
        public void setContinuousRendering (boolean isContinuous) {\r
                if (view != null) {\r
                        this.isContinuous = isContinuous;\r
                        int renderMode = isContinuous ? GLSurfaceView.RENDERMODE_CONTINUOUSLY : GLSurfaceView.RENDERMODE_WHEN_DIRTY;\r
-                       view.setRenderMode(renderMode);\r
+                       // jw: changed\r
+                       //view.setRenderMode(renderMode);\r
+                       if (view instanceof GLSurfaceViewCupcake) ((GLSurfaceViewCupcake)view).setRenderMode(renderMode);\r
+                       else if (view instanceof GLSurfaceView) ((GLSurfaceView)view).setRenderMode(renderMode);\r
+                       else throw new RuntimeException("unimplemented");\r
+                       mean.clear();\r
                }\r
        }\r
 \r
+       public boolean isContinuousRendering () {\r
+               return isContinuous;\r
+       }\r
+\r
        @Override\r
        public void requestRendering () {\r
                if (view != null) {\r
-                       view.requestRender();\r
+                       // jw: changed\r
+                       //view.requestRender();\r
+                       if (view instanceof GLSurfaceViewCupcake) ((GLSurfaceViewCupcake)view).requestRender();\r
+                       else if (view instanceof GLSurfaceView) ((GLSurfaceView)view).requestRender();\r
+                       else throw new RuntimeException("unimplemented");\r
                }\r
+       }\r
 \r
+       @Override\r
+       public boolean isFullscreen () {\r
+               return true;\r
        }\r
 }\r
index 3db7ea8..e023130 100755 (executable)
@@ -2,6 +2,7 @@
  * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com)\r
  * \r
  * Modified by Elijah Cornell\r
+ * 2013.01 Modified by Jaroslaw Wisniewski <j.wisniewski@appsisle.com>\r
  * \r
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the\r
  * License. You may obtain a copy of the License at\r
 \r
 package com.badlogic.gdx.backends.android;\r
 \r
+import java.lang.reflect.Method;\r
 import java.util.ArrayList;\r
 import java.util.List;\r
 \r
 import android.app.Activity;\r
 import android.content.Context;\r
 import android.content.res.Configuration;\r
+import android.opengl.GLSurfaceView;\r
 import android.os.Build;\r
 import android.os.Bundle;\r
 import android.os.Debug;\r
@@ -30,6 +33,7 @@ import android.service.wallpaper.WallpaperService;
 import android.service.wallpaper.WallpaperService.Engine;\r
 import android.util.Log;\r
 import android.view.View;\r
+import android.view.WindowManager;\r
 import android.widget.FrameLayout.LayoutParams;\r
 \r
 import com.badlogic.gdx.Application;\r
@@ -44,6 +48,10 @@ import com.badlogic.gdx.Net;
 import com.badlogic.gdx.Preferences;\r
 import com.badlogic.gdx.Application.ApplicationType;\r
 import com.badlogic.gdx.backends.android.surfaceview.FillResolutionStrategy;\r
+import com.badlogic.gdx.backends.android.surfaceview.GLBaseSurfaceViewLW;\r
+import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20;\r
+import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20LW;\r
+import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceViewCupcake;\r
 import com.badlogic.gdx.graphics.GL10;\r
 import com.badlogic.gdx.graphics.GL11;\r
 import com.badlogic.gdx.utils.Array;\r
@@ -57,13 +65,13 @@ import com.badlogic.gdx.utils.GdxNativesLoader;
  * \r
  * @author mzechner\r
  */\r
-class AndroidLiveWallpaper implements Application {\r
+public class AndroidLiveWallpaper implements Application {\r
        static {\r
                GdxNativesLoader.load();\r
        }\r
+       \r
+       protected AndroidLiveWallpaperService service;\r
 \r
-       protected WallpaperService service;\r
-       private Engine engine;\r
        protected AndroidGraphicsLiveWallpaper graphics;\r
        protected AndroidInput input;\r
        protected AndroidAudio audio;\r
@@ -76,72 +84,155 @@ class AndroidLiveWallpaper implements Application {
        protected final Array<LifecycleListener> lifecycleListeners = new Array<LifecycleListener>();\r
        protected int logLevel = LOG_INFO;\r
 \r
-       public AndroidLiveWallpaper(WallpaperService service, Engine engine) {\r
+       public AndroidLiveWallpaper(AndroidLiveWallpaperService service) {\r
                this.service = service;\r
-               this.engine = engine;\r
        }\r
        \r
        public void initialize(ApplicationListener listener, AndroidApplicationConfiguration config) {\r
                graphics = new AndroidGraphicsLiveWallpaper(this, config, config.resolutionStrategy==null?new FillResolutionStrategy():config.resolutionStrategy);\r
-               input = AndroidInputFactory.newAndroidInput(this, this.getService(), null, config);\r
+               \r
+               // factory in use, but note: AndroidInputFactory causes exceptions when obfuscated: java.lang.RuntimeException: Couldn't construct AndroidInput, this should never happen, proguard deletes constructor used only by reflection\r
+               input = AndroidInputFactory.newAndroidInput(this, this.getService(), graphics.view, config);\r
+               //input = new AndroidInput(this, this.getService(), null, config);\r
+\r
                audio = new AndroidAudio(this.getService(), config);\r
+               \r
+               // added initialization of android local storage: /data/data/<app package>/files/\r
                files = new AndroidFiles(this.getService().getAssets(), this.getService().getFilesDir().getAbsolutePath());\r
-               net = new AndroidNet(null);\r
+               \r
                this.listener = listener;\r
                \r
                Gdx.app = this;\r
-               Gdx.input = this.getInput();\r
-               Gdx.audio = this.getAudio();\r
-               Gdx.files = this.getFiles();\r
-               Gdx.net = this.getNet();\r
-               Gdx.graphics = this.getGraphics();\r
+               Gdx.input = input;\r
+               Gdx.audio = audio;\r
+               Gdx.files = files;\r
+               Gdx.graphics = graphics;\r
        }\r
 \r
        public void onPause() {\r
-               graphics.pause();\r
-               if (audio != null) audio.pause();\r
+               if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.TAG, " > AndroidLiveWallpaper - onPause()");\r
+\r
+               // IMPORTANT!\r
+               // jw: graphics.pause is never called, graphics.pause works on most devices but not on all.. \r
+               // for example on Samsung Galaxy Tab (GT-P6800) on android 4.0.4 invoking graphics.pause causes "Fatal Signal 11" \r
+               // near mEglHelper.swap() in GLSurfaceView while processing next onPause event.\r
+               // See related issue: \r
+               // http://code.google.com/p/libgdx/issues/detail?id=541\r
+               // the problem with graphics.pause occurs while using OpenGL 2.0 and original GLSurfaceView while rotating device in lwp preview\r
+               // in my opinion it is a bug of android not libgdx, even example Cubic live wallpaper from\r
+               // Android SDK crashes on affected devices.......... and on some configurations of android emulator too.\r
+               // \r
+               // My wallpaper was rejected on Samsung Apps because of this issue, so I decided to disable graphics.pause.. \r
+               // also I moved audio lifecycle methods from AndroidGraphicsLiveWallpaper into this class\r
+               \r
+               //graphics.pause();\r
+               //if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.TAG, " > AndroidLiveWallpaper - onPause() application paused!");\r
+               audio.pause();\r
+\r
                input.unregisterSensorListeners();\r
+               \r
+               if (graphics != null && graphics.view != null) {\r
+                       if (graphics.view instanceof GLSurfaceViewCupcake) ((GLSurfaceViewCupcake)graphics.view).onPause();\r
+                       else if (graphics.view instanceof android.opengl.GLSurfaceView) ((android.opengl.GLSurfaceView)graphics.view).onPause();\r
+                       else throw new RuntimeException("unimplemented");\r
+               }\r
+               \r
+               if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.TAG, " > AndroidLiveWallpaper - onPause() done!");\r
        }\r
 \r
        public void onResume() {\r
                Gdx.app = this;\r
-               Gdx.input = this.getInput();\r
-               Gdx.audio = this.getAudio();\r
-               Gdx.files = this.getFiles();\r
-               Gdx.net = this.getNet();\r
-               Gdx.graphics = this.getGraphics();\r
+               Gdx.input = input;\r
+               Gdx.audio = audio;\r
+               Gdx.files = files;\r
+               Gdx.graphics = graphics;\r
 \r
-               ((AndroidInput)getInput()).registerSensorListeners();\r
+               input.registerSensorListeners();\r
+               \r
+               // FIXME restore conditional execution if lifecycle errors will occur when GLSurfaceView used. \r
+               // GLSurfaceView is guaranteed to work with this condition on, but GLSurfaceViewCupcake requires it off,\r
+               // so I disabled it.\r
+               //if (!firstResume)     // mentioned condition\r
+               if (graphics != null && graphics.view != null) {\r
+                       if (graphics.view instanceof GLSurfaceViewCupcake) ((GLSurfaceViewCupcake)graphics.view).onResume();\r
+                       else if (graphics.view instanceof android.opengl.GLSurfaceView) ((android.opengl.GLSurfaceView)graphics.view).onResume();\r
+                       else throw new RuntimeException("unimplemented");\r
+               }\r
                \r
-               if (audio != null) audio.resume();              \r
                if (!firstResume)\r
+               {\r
+                       audio.resume();\r
                        graphics.resume();\r
+               }\r
                else\r
                        firstResume = false;\r
        }\r
        \r
        public void onDestroy() {\r
-               graphics.clearManagedCaches();\r
-               // graphics.destroy();\r
-       }\r
 \r
-       public WallpaperService getService() {\r
-               return service;\r
+               // it is too late to call graphics.destroy - it needs live gl GLThread and gl context, otherwise it will cause of deadlock\r
+               //if (graphics != null) {\r
+               //      graphics.clearManagedCaches();\r
+               //      graphics.destroy();\r
+               //}\r
+               \r
+               // so we do what we can..\r
+               if (graphics != null)\r
+               {\r
+                       // not necessary - already called in AndroidLiveWallpaperService.onDeepPauseApplication\r
+                       // app.graphics.clearManagedCaches();\r
+                       \r
+                       // kill the GLThread managed by GLSurfaceView (only for GLSurfaceView because GLSurffaceViewCupcake stops thread in onPause events - which is not as easy and safe for GLSurfaceView)\r
+                       if (graphics.view != null && (graphics.view instanceof GLSurfaceView))\r
+                       {\r
+                               GLSurfaceView glSurfaceView = (GLSurfaceView)graphics.view;\r
+                               try {\r
+                                       Method method = null;\r
+                                       for (Method m : glSurfaceView.getClass().getMethods()) \r
+                                       {\r
+                                               if (m.getName().equals("onDestroy"))    // implemented in AndroidGraphicsLiveWallpaper, redirects to onDetachedFromWindow - which stops GLThread by calling mGLThread.requestExitAndWait()\r
+                                               {\r
+                                                       method = m;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       if (method != null)\r
+                                       {\r
+                                               method.invoke(glSurfaceView);\r
+                                               if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.TAG, " > AndroidLiveWallpaper - onDestroy() stopped GLThread managed by GLSurfaceView");\r
+                                       }\r
+                                       else\r
+                                               throw new Exception("method not found!");\r
+                               } \r
+                               catch (Throwable t)\r
+                               {\r
+                                       // error while scheduling exit of GLThread, GLThread will remain live and wallpaper service wouldn't be able to shutdown completely\r
+                                       Log.e(AndroidLiveWallpaperService.TAG, "failed to destroy GLSurfaceView's thread! GLSurfaceView.onDetachedFromWindow impl changed since API lvl 16!");\r
+                                       t.printStackTrace();\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               if (audio != null)\r
+               {\r
+                       // dispose audio and free native resources, mandatory since graphics.pause is never called in live wallpaper\r
+                       audio.dispose();\r
+               }\r
        }\r
 \r
-       public Engine getEngine() {\r
-               return engine;\r
+       public WindowManager getWindowManager() {\r
+               return service.getWindowManager();\r
+       }\r
+       \r
+       public AndroidLiveWallpaperService getService() {\r
+               return service;\r
        }\r
 \r
-       /** @deprecated Use {@link #getApplicationListener()} instead */\r
        public ApplicationListener getListener() {\r
                return listener;\r
        }\r
 \r
-       public ApplicationListener getApplicationListener() {\r
-               return listener;\r
-       }\r
-       \r
        @Override \r
        public void postRunnable (Runnable runnable) {\r
                synchronized(runnables) {\r
@@ -266,4 +357,9 @@ class AndroidLiveWallpaper implements Application {
                        lifecycleListeners.removeValue(listener, true);\r
                }               \r
        }\r
+\r
+       @Override\r
+       public ApplicationListener getApplicationListener () {\r
+               return listener;\r
+       }\r
 }\r
index 4524882..2397264 100755 (executable)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright 2010 Elijah Cornell\r
+ * Copyright 2013 Jaroslaw Wisniewski <j.wisniewski@appsisle.com>\r
  * \r
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the\r
  * License. You may obtain a copy of the License at\r
 \r
 package com.badlogic.gdx.backends.android;\r
 \r
+import java.lang.reflect.Method;\r
+import java.util.concurrent.locks.ReentrantLock;\r
+\r
+import com.badlogic.gdx.Application;\r
 import com.badlogic.gdx.ApplicationListener;\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.Graphics;\r
+import com.badlogic.gdx.android.AndroidWallpaperListener;\r
+import com.badlogic.gdx.backends.android.surfaceview.FillResolutionStrategy;\r
 import com.badlogic.gdx.backends.android.surfaceview.GLBaseSurfaceViewLW;\r
+import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceViewCupcake;\r
+import com.badlogic.gdx.graphics.GL10;\r
+import com.badlogic.gdx.graphics.GL11;\r
+import com.badlogic.gdx.utils.GdxNativesLoader;\r
 \r
+import android.app.Activity;\r
 import android.app.WallpaperManager;\r
+import android.content.Context;\r
+import android.opengl.GLSurfaceView;\r
 import android.os.Bundle;\r
+import android.os.Handler;\r
+import android.provider.LiveFolders;\r
 import android.service.wallpaper.WallpaperService;\r
 import android.service.wallpaper.WallpaperService.Engine;\r
 import android.util.Log;\r
 import android.view.MotionEvent;\r
 import android.view.SurfaceHolder;\r
+import android.view.WindowManager;\r
 \r
 \r
+/** \r
+ * An implementation of the {@link Application} interface dedicated for android live wallpapers.\r
+ * \r
+ * Derive from this class. In the {@link AndroidLiveWallpaperService#onCreateApplication} method call the {@link AndroidLiveWallpaperService#initialize(ApplicationListener, boolean)} \r
+ * method specifying the configuration for the GLSurfaceView. You can also use {@link AndroidWallpaperListener} \r
+ * along with {@link ApplicationListener} to respond for wallpaper specific events in your app listener:\r
+ * \r
+ * MyAppListener implements ApplicationListener, AndroidWallpaperListener\r
+ * \r
+ * Notice:\r
+ * Following methods are not called for live wallpapers:\r
+ * {@link ApplicationListener#pause()}\r
+ * {@link ApplicationListener#dispose()}\r
+ * TODO add callbacks to AndroidWallpaperListener allowing to notify app listener about changed visibility\r
+ * state of live wallpaper but called from main thread, not from GL thread:\r
+ * for example:\r
+ * AndroidWallpaperListener.visibilityChanged(boolean)\r
+ * \r
+ * //obsoleted:\r
+ * //Notice!\r
+ * //You have to kill all not daemon threads you created in {@link ApplicationListener#pause()} method.\r
+ * //{@link ApplicationListener#dispose()} is never called!\r
+ * //If you leave live non daemon threads, wallpaper service wouldn't be able to close, \r
+ * //this can cause problems with wallpaper lifecycle.\r
+ * \r
+ * Notice #2!\r
+ * On some devices wallpaper service is not killed immediately after exiting from preview. Service object \r
+ * is destroyed (onDestroy called) but process on which it runs remains alive. When user comes back to wallpaper\r
+ * preview, new wallpaper service object is created, but in the same process. It is important if you plan to\r
+ * use static variables / objects - they will be shared between living instances of wallpaper services'!\r
+ * And depending on your implementation - it can cause problems you were not prepared to.\r
+ * \r
+ * @author Jaroslaw Wisniewski <j.wisniewski@appsisle.com>\r
+ */\r
 public abstract class AndroidLiveWallpaperService extends WallpaperService {\r
-       final String TAG = "AndroidLiveWallpaperService";\r
-       static boolean DEBUG = false;\r
-       protected static volatile int runningEngines = 0;\r
+       static {\r
+               GdxNativesLoader.load();\r
+       }\r
+       \r
+       static final String TAG = "WallpaperService";\r
+       static boolean DEBUG    = false;        // TODO remember to disable this\r
+\r
+       \r
+       // instance of libGDX Application, acts as singleton - one instance per application (per WallpaperService)\r
+       protected volatile AndroidLiveWallpaper app = null;     // can be accessed from GL render thread\r
+       protected SurfaceHolder.Callback view = null;\r
+       \r
+       // current format of surface (one GLSurfaceView is shared between all engines)\r
+       protected int viewFormat;\r
+       protected int viewWidth;\r
+       protected int viewHeight;\r
+       \r
+       // app is initialized when engines == 1 first time, app is destroyed in WallpaperService.onDestroy, but ApplicationListener.dispose is not called for wallpapers\r
+       protected int engines = 0;\r
+       protected int visibleEngines = 0;\r
+       \r
+       // engine currently associated with app instance, linked engine serves surface handler for GLSurfaceView\r
+       protected volatile AndroidWallpaperEngine linkedEngine = null;          // can be accessed from GL render thread by getSurfaceHolder\r
+       \r
+       protected void setLinkedEngine (AndroidWallpaperEngine linkedEngine) {\r
+               synchronized (sync) {\r
+                       this.linkedEngine = linkedEngine;\r
+               }\r
+       }\r
+       \r
+       \r
+       // if preview state notified ever\r
+       protected volatile boolean isPreviewNotified = false;\r
+       \r
+       // the value of last preview state notified to app listener\r
+       protected volatile boolean notifiedPreviewState = false;\r
 \r
+\r
+       volatile int[] sync = new int[0];\r
+       //volatile ReentrantLock lock = new ReentrantLock();\r
+       \r
+       \r
+       // lifecycle methods - the order of calling (flow) is maintained ///////////////\r
+       \r
        public AndroidLiveWallpaperService () {\r
                super();\r
        }\r
 \r
+       \r
+       /**\r
+        * Service is starting, libGDX application is shutdown now\r
+        */\r
        @Override\r
        public void onCreate () {\r
-               if (DEBUG) Log.d(TAG, " > LibdgxWallpaperService - onCreate()");\r
-               super.onCreate();\r
-       }\r
+               if (DEBUG) Log.d(TAG, " > AndroidLiveWallpaperService - onCreate() " + hashCode());\r
+               Log.i(TAG, "service created");\r
 \r
+               super.onCreate();               \r
+       }\r
+       \r
+       \r
+       /**\r
+        * One of wallpaper engines is starting. \r
+        * Do not override this method, service manages them internally.\r
+        */\r
        @Override\r
        public Engine onCreateEngine () {\r
+               if (DEBUG) Log.d(TAG, " > AndroidLiveWallpaperService - onCreateEngine()");\r
+               Log.i(TAG, "engine created");\r
+               \r
                return new AndroidWallpaperEngine();\r
        }\r
        \r
+       \r
        /**\r
-        * @return a new {@link ApplicationListener} that implements the live wallpaper\r
+        * libGDX application is starting, it occurs after first wallpaper engine had started.\r
+        * Override this method an invoke {@link AndroidLiveWallpaperService#initialize(ApplicationListener, AndroidApplicationConfiguration)} from there.\r
         */\r
-       public abstract ApplicationListener createListener(boolean isPreview); \r
+       public void onCreateApplication () {\r
+               if (DEBUG) Log.d(TAG, " > AndroidLiveWallpaperService - onCreateApplication()");\r
+       }\r
+       \r
+       \r
+       /** \r
+        * Look at {@link AndroidLiveWallpaperService#initialize(ApplicationListener, AndroidApplicationConfiguration)}}\r
+        * @param listener\r
+        * @param useGL2IfAvailable\r
+        */\r
+       public void initialize (ApplicationListener listener, boolean useGL2IfAvailable) {\r
+               AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();\r
+               config.useGL20 = useGL2IfAvailable;\r
+               initialize(listener, config);\r
+       }\r
+\r
+       /** \r
+        * This method has to be called in the {@link AndroidLiveWallpaperService#onCreateApplication} method. It sets up all the things necessary to get\r
+        * input, render via OpenGL and so on. If config.useGL20 is set the AndroidApplication will try to create an OpenGL ES 2.0\r
+        * context which can then be used via {@link Graphics#getGL20()}. The {@link GL10} and {@link GL11} interfaces should not be\r
+        * used when OpenGL ES 2.0 is enabled. To query whether enabling OpenGL ES 2.0 was successful use the\r
+        * {@link Graphics#isGL20Available()} method. You can configure other aspects of the application with the rest of the fields in\r
+        * the {@link AndroidApplicationConfiguration} instance.\r
+        * \r
+        * @param listener the {@link ApplicationListener} implementing the program logic\r
+        * @param config the {@link AndroidApplicationConfiguration}, defining various settings of the application (use accelerometer,\r
+        *           etc.). Do not change contents of this object after passing to this method!\r
+        */\r
+       public void initialize (ApplicationListener listener, AndroidApplicationConfiguration config) {\r
+               if (DEBUG) Log.d(TAG, " > AndroidLiveWallpaperService - initialize()");\r
+               \r
+               app.initialize(listener, config);\r
+               \r
+               if (config.getTouchEventsForLiveWallpaper && Integer.parseInt(android.os.Build.VERSION.SDK) < 9)\r
+                       linkedEngine.setTouchEventsEnabled(true);\r
+               \r
+               //onResume(); do not call it there\r
+       }\r
+       \r
        \r
        /**\r
-        * @return a new {@link AndroidApplicationConfiguration} that specifies the config to be used for the live wall paper\r
+        * Getter for SurfaceHolder object, surface holder is required to restore gl context in GLSurfaceView\r
         */\r
-       public abstract AndroidApplicationConfiguration createConfig();\r
+       public SurfaceHolder getSurfaceHolder() {\r
+               if (DEBUG) Log.d(TAG, " > AndroidLiveWallpaperService - getSurfaceHolder()");\r
+               \r
+               synchronized (sync) {\r
+                       if (linkedEngine == null)\r
+                               return null;\r
+                       else \r
+                               return linkedEngine.getSurfaceHolder();\r
+               }\r
+       }\r
+       \r
+       \r
+       // engines live there\r
+       \r
        \r
        /**\r
-        * Called when the live wallpaper's offset changed. This method will be called\r
-        * on the rendering thread.\r
-        * @param xOffset\r
-        * @param yOffset\r
-        * @param xOffsetStep\r
-        * @param yOffsetStep\r
-        * @param xPixelOffset\r
-        * @param yPixelOffset\r
+        * Called when the last engine is ending its live, it can occur when:\r
+        * 1. service is dying\r
+        * 2. service is switching from one engine to another\r
+        * 3. [only my assumption] when wallpaper is not visible and system is going to restore some memory \r
+        *      for foreground processing by disposing not used wallpaper engine\r
+        * We can't destroy app there, because:\r
+        * 1. in won't work - gl context is disposed right now and after app.onDestroy() app would stuck somewhere in gl thread synchronizing code\r
+        * 2. we don't know if service create more engines, app is shared between them and should stay initialized waiting for new engines\r
         */\r
-       public abstract void offsetChange (ApplicationListener listener, float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset);\r
+       public void onDeepPauseApplication () {\r
+               if (DEBUG) Log.d(TAG, " > AndroidLiveWallpaperService - onDeepPauseApplication()");\r
+               \r
+               // free native resources consuming runtime memory, note that it can cause some lag when resuming wallpaper\r
+               if (app != null) {\r
+                       app.graphics.clearManagedCaches();\r
+               }\r
+       }\r
+       \r
        \r
+       /**\r
+        * Service is dying, and will not be used again.\r
+        * You have to finish execution off all living threads there or short after there, \r
+        * besides the new wallpaper service wouldn't be able to start.\r
+        */\r
        @Override\r
        public void onDestroy () {\r
-               if (DEBUG) Log.d(TAG, " > LibdgxWallpaperService - onDestroy()");\r
-               super.onDestroy();\r
+               if (DEBUG) Log.d(TAG, " > AndroidLiveWallpaperService - onDestroy() " + hashCode());\r
+               Log.i(TAG, "service destroyed");\r
+               \r
+               super.onDestroy();      // can call engine.onSurfaceDestroyed, must be before bellow code:\r
+               \r
+               if (app != null) {\r
+                       app.onDestroy();\r
+                       \r
+                       app = null;\r
+                       view = null;\r
+               }\r
        }\r
        \r
+       \r
+       @Override\r
+       protected void finalize () throws Throwable {\r
+               Log.i(TAG, "service finalized");\r
+               super.finalize();\r
+       }\r
+       \r
+       // end of lifecycle methods ////////////////////////////////////////////////////////\r
+       \r
+       \r
+       \r
+       public AndroidLiveWallpaper getLiveWallpaper() {\r
+               return app;\r
+       }\r
+       \r
+       \r
+       public WindowManager getWindowManager() {\r
+               return (WindowManager)getSystemService(Context.WINDOW_SERVICE);\r
+       }\r
+       \r
+       \r
+       /**\r
+        * Bridge between surface on which wallpaper is rendered and the wallpaper service. \r
+        * The problem is that there can be a group of Engines at one time and we must share libGDX application between them.\r
+        * \r
+        * @author libGDX team and Jaroslaw Wisniewski <j.wisniewski@appsisle.com>\r
+        *\r
+        */\r
        public class AndroidWallpaperEngine extends Engine {\r
-               protected AndroidLiveWallpaper app;\r
-               protected ApplicationListener listener;\r
-               protected GLBaseSurfaceViewLW view;\r
 \r
+               protected boolean engineIsVisible = false;\r
+               \r
+               // destination format of surface when this engine is active (updated in onSurfaceChanged)\r
+               protected int engineFormat;\r
+               protected int engineWidth;\r
+               protected int engineHeight;\r
+               \r
+               \r
+               // lifecycle methods - the order of calling (flow) is maintained /////////////////\r
+               \r
                public AndroidWallpaperEngine () {\r
-                       if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.this.TAG, " > MyEngine() " + hashCode());\r
+                       if (DEBUG) Log.d(TAG, " > AndroidWallpaperEngine() " + hashCode());\r
                }\r
 \r
+               \r
                @Override\r
-               public Bundle onCommand (final String pAction, final int pX, final int pY, final int pZ, final Bundle pExtras,\r
-                       final boolean pResultRequested) {\r
+               public void onCreate (final SurfaceHolder surfaceHolder) {\r
+                       if (DEBUG) Log.d(TAG, " > AndroidWallpaperEngine - onCreate() " + hashCode() + " running: " + engines + ", linked: " + (linkedEngine == this) + ", thread: " + Thread.currentThread().toString());\r
+                       super.onCreate(surfaceHolder);\r
+               }\r
+               \r
+               \r
+               /**\r
+                * Called before surface holder callbacks (ex for GLSurfaceView)!\r
+                * This is called immediately after the surface is first created. Implementations of this should start \r
+                * up whatever rendering code they desire. Note that only one thread can ever draw into a Surface, \r
+                * so you should not draw into the Surface here if your normal rendering will be in another thread.\r
+                */\r
+               @Override\r
+               public void onSurfaceCreated (final SurfaceHolder holder) {\r
+                       engines ++;\r
+                       setLinkedEngine(this);\r
+                       \r
+                       if (DEBUG) Log.d(TAG, " > AndroidWallpaperEngine - onSurfaceCreated() " + hashCode() + ", running: " + engines + ", linked: " + (linkedEngine == this));\r
+                       Log.i(TAG, "engine surface created");\r
+                       \r
+                       super.onSurfaceCreated(holder);\r
+       \r
+                       if (engines == 1) {\r
+                               // safeguard: recover attributes that could suffered by unexpected surfaceDestroy event\r
+                               visibleEngines = 0;\r
+                       }\r
+                       \r
+                       if (engines == 1 && app == null) {\r
+                               viewFormat = 0; // must be initialized with zeroes\r
+                               viewWidth = 0;\r
+                               viewHeight = 0;\r
+                               \r
+                               app = new AndroidLiveWallpaper(AndroidLiveWallpaperService.this);\r
+                               \r
+                               onCreateApplication();\r
+                               if (app.graphics == null)\r
+                                       throw new Error("You must override 'AndroidLiveWallpaperService.onCreateApplication' method and call 'initialize' from its body.");\r
+                               \r
+                               view = (SurfaceHolder.Callback)app.graphics.view;\r
+                               this.getSurfaceHolder().removeCallback(view);   // we are going to call this events manually\r
+                       }\r
 \r
-                       if (AndroidLiveWallpaperService.DEBUG)\r
-                               Log.d(TAG, " > onCommand(" + pAction + " " + pX + " " + pY + " " + pZ + " " + pExtras + " "\r
-                                       + pResultRequested + ")");\r
+                       // inherit format from shared surface view\r
+                       engineFormat = viewFormat;\r
+                       engineWidth = viewWidth;\r
+                       engineHeight = viewHeight;\r
 \r
-                       // FIXME\r
-//                     if (pAction.equals(WallpaperManager.COMMAND_TAP)) {\r
-//                             app.input.onTap(pX, pY);\r
-//                     } else if (pAction.equals(WallpaperManager.COMMAND_DROP)) {\r
-//                             app.input.onDrop(pX, pY);\r
-//                     }\r
-                       return super.onCommand(pAction, pX, pY, pZ, pExtras, pResultRequested);\r
+                       if (engines == 1) {\r
+                               view.surfaceCreated(holder);\r
+                       }\r
+                       else {\r
+                               // this combination of methods is described in AndroidWallpaperEngine.onResume\r
+                               view.surfaceDestroyed(holder);\r
+                               notifySurfaceChanged(engineFormat, engineWidth, engineHeight, false);\r
+                               view.surfaceCreated(holder);\r
+                       }\r
+                       \r
+                       notifyPreviewState();\r
+                       notifyOffsetsChanged();\r
                }\r
-\r
+               \r
+               \r
+               /**\r
+                * This is called immediately after any structural changes (format or size) have been made to the surface. \r
+                * You should at this point update the imagery in the surface. This method is always called at least once, \r
+                * after surfaceCreated(SurfaceHolder).\r
+                */\r
                @Override\r
-               public void onCreate (final SurfaceHolder surfaceHolder) {\r
-                       runningEngines++;\r
-                       if (AndroidLiveWallpaperService.DEBUG) Log.d(TAG, " > onCreate() " + hashCode() + ", running: " + runningEngines);\r
-                       super.onCreate(surfaceHolder);\r
-                       this.app = new AndroidLiveWallpaper(AndroidLiveWallpaperService.this, this);\r
-                       AndroidApplicationConfiguration config = createConfig();\r
-                       listener = createListener(isPreview());\r
-                       this.app.initialize(listener, config);\r
-                       this.view = ((AndroidGraphicsLiveWallpaper)app.getGraphics()).getView();\r
+               public void onSurfaceChanged (final SurfaceHolder holder, final int format, final int width, final int height) {\r
+                       if (DEBUG) Log.d(TAG, " > AndroidWallpaperEngine - onSurfaceChanged() isPreview: " + isPreview() + ", " + hashCode() + ", running: " + engines + ", linked: " + (linkedEngine == this) + ", sufcace valid: " + getSurfaceHolder().getSurface().isValid());\r
+                       Log.i(TAG, "engine surface changed");\r
+                       \r
+                       super.onSurfaceChanged(holder, format, width, height);\r
+                       \r
+                       notifySurfaceChanged(format, width, height, true);\r
+                       \r
+                       // it shouldn't be required there (as I understand android.service.wallpaper.WallpaperService impl)\r
+                       //notifyPreviewState();\r
+               }\r
 \r
-                       if (config.getTouchEventsForLiveWallpaper && Integer.parseInt(android.os.Build.VERSION.SDK) < 9)\r
-                               this.setTouchEventsEnabled(true);\r
+               \r
+               /**\r
+                * Notifies shared GLSurfaceView about changed surface format.\r
+                * @param format\r
+                * @param width\r
+                * @param height\r
+                * @param forceUpdate if false, surface view will be notified only if currently contains expired information\r
+                */\r
+               private void notifySurfaceChanged(final int format, final int width, final int height, boolean forceUpdate)\r
+               {\r
+                       if (!forceUpdate && format == viewFormat && width == viewWidth && height == viewHeight) {\r
+                               // skip if didn't changed\r
+                               if (DEBUG) Log.d(TAG, " > surface is current, skipping surfaceChanged event");\r
+                       }\r
+                       else {\r
+                               // update engine desired surface format\r
+                               engineFormat = format;\r
+                               engineWidth = width;\r
+                               engineHeight = height;\r
+                               \r
+                               // update surface view if engine is linked with it already\r
+                               if (linkedEngine == this) {\r
+                                       viewFormat = engineFormat;\r
+                                       viewWidth = engineWidth;\r
+                                       viewHeight = engineHeight;\r
+                                       view.surfaceChanged(this.getSurfaceHolder(), viewFormat, viewWidth, viewHeight);\r
+                               }\r
+                               else {\r
+                                       if (DEBUG) Log.d(TAG, " > engine is not active, skipping surfaceChanged event");\r
+                               }\r
+                       }\r
                }\r
+               \r
 \r
+               /**\r
+                * Called to inform you of the wallpaper becoming visible or hidden. It is very important that \r
+                * a wallpaper only use CPU while it is visible..\r
+                */\r
                @Override\r
-               public void onDestroy () {\r
-                       runningEngines--;\r
-                       if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.this.TAG, " > onDestroy() " + hashCode() + ", running: " + runningEngines);\r
-                       view.onDestroy();\r
-                       if (listener != null)\r
-                               listener.dispose();\r
-                       app.onDestroy();\r
-                       super.onDestroy();\r
-               }\r
+               public void onVisibilityChanged (final boolean visible) {\r
+                       boolean reportedVisible = isVisible();\r
 \r
-               public void onPause () {\r
-                       if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.this.TAG, " > onPause() " + hashCode() + ", running: " + runningEngines);\r
-                       app.onPause();\r
-                       view.onPause();\r
-               }\r
+                       if (DEBUG) Log.d(TAG, " > AndroidWallpaperEngine - onVisibilityChanged(paramVisible: " + visible + " reportedVisible: " + reportedVisible + ") " + hashCode()  + ", sufcace valid: " + getSurfaceHolder().getSurface().isValid());\r
+                       super.onVisibilityChanged(visible);\r
 \r
-               public void onResume () {\r
-                       if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.this.TAG, " > onResume() " + hashCode() + ", running: " + runningEngines);\r
-                       app.onResume();\r
-                       view.onResume();\r
+                       // Android WallpaperService sends fake visibility changed events to force some buggy live wallpapers to shut down after onSurfaceChanged when they aren't visible, it can cause problems in current implementation and it is not necessary\r
+                       if (reportedVisible == false && visible == true) {\r
+                               if (DEBUG) Log.d(TAG, " > fake visibilityChanged event! Android WallpaperService likes do that!");\r
+                               return;\r
+                       }\r
+\r
+                       notifyVisibilityChanged(visible);\r
                }\r
 \r
-               @Override\r
-               public void onSurfaceChanged (final SurfaceHolder holder, final int format, final int width, final int height) {\r
-                       if (AndroidLiveWallpaperService.DEBUG)\r
-                               Log.d(AndroidLiveWallpaperService.this.TAG, " > onSurfaceChanged() " + isPreview() + " " + hashCode() + ", running: " + runningEngines);\r
-                       super.onSurfaceChanged(holder, format, width, height);\r
+               \r
+               private void notifyVisibilityChanged(final boolean visible)\r
+               {\r
+                       if (this.engineIsVisible != visible) {\r
+                               this.engineIsVisible = visible;\r
+                               \r
+                               if (this.engineIsVisible)\r
+                                       onResume();\r
+                               else\r
+                                       onPause();\r
+                       }\r
+                       else {\r
+                               if (DEBUG) Log.d(TAG, " > visible state is current, skipping visibilityChanged event!");\r
+                       }\r
                }\r
+               \r
+               \r
+               public void onResume () {\r
+                       visibleEngines ++;\r
+                       if (DEBUG) Log.d(TAG, " > AndroidWallpaperEngine - onResume() " + hashCode() + ", running: " + engines + ", linked: " + (linkedEngine == this) + ", visible: " + visibleEngines);\r
+                       Log.i(TAG, "engine resumed");\r
+                       \r
+                       if (linkedEngine != null) {\r
+                               if (linkedEngine != this) {\r
+                                       setLinkedEngine(this);\r
+                                       \r
+                                       // disconnect surface view from previous window\r
+                                       view.surfaceDestroyed(this.getSurfaceHolder()); // force gl surface reload, new instance will be created on current surface holder\r
+                                       \r
+                                       // resize surface to match window associated with current engine\r
+                                       notifySurfaceChanged(engineFormat, engineWidth, engineHeight, false);\r
 \r
-               @Override\r
-               public void onSurfaceCreated (final SurfaceHolder holder) {\r
-                       if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.this.TAG, " > onSurfaceCreated() " + hashCode() + ", running: " + runningEngines);\r
-                       super.onSurfaceCreated(holder);\r
+                                       // connect surface view to current engine\r
+                                       view.surfaceCreated(this.getSurfaceHolder());\r
+                               }\r
+                               else {\r
+                                       // update if surface changed when engine wasn't active\r
+                                       notifySurfaceChanged(engineFormat, engineWidth, engineHeight, false);\r
+                               }\r
+                               \r
+                               if (visibleEngines == 1)\r
+                                       app.onResume();\r
+\r
+                               notifyPreviewState();\r
+                               notifyOffsetsChanged();\r
+                       }\r
+               }\r
+               \r
+               \r
+               public void onPause () {\r
+                       visibleEngines --;\r
+                       if (DEBUG) Log.d(TAG, " > AndroidWallpaperEngine - onPause() " + hashCode() + ", running: " + engines + ", linked: " + (linkedEngine == this) + ", visible: " + visibleEngines);\r
+                       Log.i(TAG, "engine paused");\r
+                       \r
+                       // this shouldn't never happen, but if it will.. live wallpaper will not be stopped when device will pause and lwp will drain battery.. shortly!\r
+                       if (visibleEngines >= engines) {\r
+                               Log.e(AndroidLiveWallpaperService.TAG, "wallpaper lifecycle error, counted too many visible engines! repairing..");\r
+                               visibleEngines = Math.max(engines - 1, 0);\r
+                       }\r
+                       \r
+                       if (linkedEngine != null) {\r
+                               if (visibleEngines == 0)\r
+                                       app.onPause();\r
+                       }\r
+                       \r
+                       if (DEBUG) Log.d(TAG, " > AndroidWallpaperEngine - onPause() done!");\r
                }\r
 \r
+               \r
+               /**\r
+                * Called after surface holder callbacks (ex for GLSurfaceView)!\r
+                * This is called immediately before a surface is being destroyed. After returning from this call, \r
+                * you should no longer try to access this surface. If you have a rendering thread that directly \r
+                * accesses the surface, you must ensure that thread is no longer touching the Surface before \r
+                * returning from this function.\r
+                * \r
+                * Attention!\r
+                * In some cases GL context may be shutdown right now! and SurfaceHolder.Surface.isVaild = false\r
+                */\r
                @Override\r
                public void onSurfaceDestroyed (final SurfaceHolder holder) {\r
-                       if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.this.TAG, " > onSurfaceDestroyed() " + hashCode()  + ", running: " + runningEngines);\r
+                       engines --;\r
+                       if (DEBUG) Log.d(TAG, " > AndroidWallpaperEngine - onSurfaceDestroyed() " + hashCode()  + ", running: " + engines + " ,linked: " + (linkedEngine == this) + ", isVisible: " + engineIsVisible);\r
+                       Log.i(TAG, "engine surface destroyed");\r
+                       \r
+                       // application can be in resumed state at this moment if app surface had been lost just after it was created (wallpaper selected too fast from preview mode etc)\r
+                       // it is too late probably - calling on pause causes deadlock\r
+                       //notifyVisibilityChanged(false);\r
+                       \r
+                       // it is too late to call app.onDispose, just free native resources\r
+                       if (engines == 0)\r
+                               onDeepPauseApplication();\r
+                       \r
+                       // free surface if it belongs to this engine and if it was initialized\r
+                       if (linkedEngine == this && view != null)\r
+                               view.surfaceDestroyed(holder);\r
+               \r
+                       //waitingSurfaceChangedEvent = null;\r
+                       engineFormat = 0;\r
+                       engineWidth = 0;\r
+                       engineHeight = 0;\r
+                       \r
+                       // safeguard for other engine callbacks\r
+                       if (engines == 0)\r
+                               linkedEngine = null;\r
+                       \r
                        super.onSurfaceDestroyed(holder);\r
                }\r
-\r
+               \r
+               \r
                @Override\r
-               public void onVisibilityChanged (final boolean visible) {\r
-                       if (AndroidLiveWallpaperService.DEBUG)\r
-                               Log.d(AndroidLiveWallpaperService.this.TAG, " > onVisibilityChanged(" + visible + ") " + hashCode());\r
-                       if (visible) {\r
-                               onResume();\r
-                       } else {\r
-                               onPause();\r
-                       }\r
-\r
-                       super.onVisibilityChanged(visible);\r
+               public void onDestroy () {\r
+                       super.onDestroy();\r
                }\r
 \r
+               // end of lifecycle methods ////////////////////////////////////////////////////////\r
+               \r
+               \r
+               // input\r
+               \r
+               @Override\r
+               public Bundle onCommand (final String pAction, final int pX, final int pY, final int pZ, final Bundle pExtras,\r
+                       final boolean pResultRequested) {\r
+                       if (DEBUG) Log.d(TAG, " > AndroidWallpaperEngine - onCommand(" + pAction + " " + pX + " " + pY + " " + pZ + " " + pExtras + " " + pResultRequested + ")" + ", linked: " + (linkedEngine == this));\r
+                       \r
+                       return super.onCommand(pAction, pX, pY, pZ, pExtras, pResultRequested);\r
+               }\r
+               \r
+               \r
                @Override\r
                public void onTouchEvent (MotionEvent event) {\r
-                       app.input.onTouch(null, event);\r
+                       if (linkedEngine == this) {\r
+                               app.input.onTouch(null, event);\r
+                       }\r
                }\r
+               \r
+               \r
+               // offsets from last onOffsetsChanged\r
+               boolean offsetsConsumed = true;\r
+               float xOffset = 0.0f;\r
+               float yOffset = 0.0f;\r
+               float xOffsetStep = 0.0f;\r
+               float yOffsetStep = 0.0f;\r
+               int xPixelOffset = 0;\r
+               int yPixelOffset = 0;\r
 \r
                @Override\r
                public void onOffsetsChanged (final float xOffset, final float yOffset, final float xOffsetStep, final float yOffsetStep, final int xPixelOffset,\r
                        final int yPixelOffset) {\r
 \r
-                       if (AndroidLiveWallpaperService.DEBUG)\r
-                               Log.d(AndroidLiveWallpaperService.this.TAG, " > onOffsetChanged(" + xOffset + " " + yOffset + " " + xOffsetStep + " "\r
-                                       + yOffsetStep + " " + xPixelOffset + " " + yPixelOffset + ") " + hashCode());\r
+                       // it spawns too frequent on some devices - its annoying!\r
+                       //if (DEBUG)\r
+                       //      Log.d(TAG, " > AndroidWallpaperEngine - onOffsetChanged(" + xOffset + " " + yOffset + " " + xOffsetStep + " "\r
+                       //              + yOffsetStep + " " + xPixelOffset + " " + yPixelOffset + ") " + hashCode() + ", linkedApp: " + (linkedApp != null));\r
 \r
-                       app.postRunnable(new Runnable() {\r
-                               @Override\r
-                               public void run () {\r
-                                       AndroidLiveWallpaperService.this.offsetChange(listener, xOffset, yOffset, xOffsetStep, yOffsetStep, xPixelOffset, yPixelOffset);        \r
-                               }\r
-                       });\r
+                       this.offsetsConsumed = false;\r
+                       this.xOffset = xOffset;\r
+                       this.yOffset = yOffset;\r
+                       this.xOffsetStep = xOffsetStep;\r
+                       this.yOffsetStep = yOffsetStep;\r
+                       this.xPixelOffset = xPixelOffset;\r
+                       this.yPixelOffset = yPixelOffset;\r
+                       \r
+                       // can fail if linkedApp == null, so we repeat it in Engine.onResume\r
+                       notifyOffsetsChanged();\r
+                       \r
                        super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixelOffset, yPixelOffset);\r
                }\r
+               \r
+               \r
+               protected void notifyOffsetsChanged()\r
+               {\r
+                       if (linkedEngine == this && app.listener instanceof AndroidWallpaperListener) {\r
+                               if (!offsetsConsumed) {         // no need for more sophisticated synchronization - offsetsChanged can be called multiple times and with various patterns on various devices - user application must be prepared for that\r
+                                       offsetsConsumed = true;\r
+                                       \r
+                                       app.postRunnable(new Runnable() {\r
+                                               @Override\r
+                                               public void run () {\r
+                                                       boolean isCurrent = false;\r
+                                                       synchronized (sync) {\r
+                                                               isCurrent = (linkedEngine == AndroidWallpaperEngine.this);              // without this app can crash when fast switching between engines (tested!)\r
+                                                       }\r
+                                                       if (isCurrent)\r
+                                                               ((AndroidWallpaperListener)app.listener).offsetChange(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixelOffset, yPixelOffset);  \r
+                                               }\r
+                                       });\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               \r
+               protected void notifyPreviewState()\r
+               {\r
+                       // notify preview state to app listener\r
+                       if (linkedEngine == this && app.listener instanceof AndroidWallpaperListener) {\r
+                               final boolean currentPreviewState = linkedEngine.isPreview();\r
+                               app.postRunnable(new Runnable() {\r
+                                       @Override\r
+                                       public void run () {\r
+                                               boolean shouldNotify = false;\r
+                                               synchronized (sync) {\r
+                                                       if (!isPreviewNotified || notifiedPreviewState != currentPreviewState) {\r
+                                                               notifiedPreviewState = currentPreviewState;\r
+                                                               isPreviewNotified = true;\r
+                                                               shouldNotify = true;\r
+                                                       }\r
+                                               }\r
+                                               \r
+                                               if (shouldNotify) {\r
+                                                       AndroidLiveWallpaper currentApp = app;          // without this app can crash when fast switching between engines (tested!)\r
+                                                       if (currentApp != null)\r
+                                                               ((AndroidWallpaperListener)currentApp.listener).previewStateChange(currentPreviewState);\r
+                                               }\r
+                                       }\r
+                               });\r
+                       }\r
+               }\r
        }       \r
 }\r
diff --git a/gdx/src/com/badlogic/gdx/android/AndroidWallpaperListener.java b/gdx/src/com/badlogic/gdx/android/AndroidWallpaperListener.java
new file mode 100644 (file)
index 0000000..a7e1160
--- /dev/null
@@ -0,0 +1,58 @@
+/*\r
+ * Copyright 2013 Jaroslaw Wisniewski <j.wisniewski@appsisle.com>\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the\r
+ * License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"\r
+ * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language\r
+ * governing permissions and limitations under the License.\r
+ *\r
+ */\r
+\r
+package com.badlogic.gdx.android;\r
+\r
+import com.badlogic.gdx.ApplicationListener;\r
+\r
+/**\r
+ * Implement this listener in your libGDX application additionally to {@link ApplicationListener} if you want \r
+ * receive live wallpaper specific events, ex:\r
+ * MyApplication implements ApplicationListener, AndroidWallpaperListener\r
+ * \r
+ * Notice!\r
+ * This callbacks will work only if app is running as android live wallpaper: \r
+ * you have to link application with AndroidLiveWallpaperService from\r
+ * in gdx-android-backend\r
+ * \r
+ * Notice libGDX developers!\r
+ * If you do not like android specific classes in gdx backend, you can rename this class to for example:\r
+ * com.badlogic.gdx.WallpaperListener so it will be 'generic' and not 'android specific', \r
+ * but besides of point of view the fact is that live wallpapers are available only on android devices so far.\r
+ * \r
+ * @author Jaroslaw Wisniewski <j.wisniewski@appsisle.com>\r
+ *\r
+ */\r
+public interface AndroidWallpaperListener {\r
+\r
+       /**\r
+        * Called on the rendering thread after the live wallpaper's offset had changed.\r
+        * @param xOffset\r
+        * @param yOffset\r
+        * @param xOffsetStep\r
+        * @param yOffsetStep\r
+        * @param xPixelOffset\r
+        * @param yPixelOffset\r
+        */\r
+       abstract void offsetChange (float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset);\r
+\r
+       /**\r
+        * Called after 'isPreview' state had changed. First time called just after application initialization.\r
+        * @param isPreview current status, save this value and update always when this method is called if you want\r
+        * track live wallpaper isPreview status.\r
+        * \r
+        * \r
+        */\r
+       abstract void previewStateChange (boolean isPreview);\r
+}\r
index c9437a9..ab6083d 100644 (file)
@@ -125,6 +125,46 @@ public class Mesh implements Disposable {
                addManagedMesh(Gdx.app, this);\r
        }\r
 \r
+       /** by jw:\r
+        * Creates a new Mesh with the given attributes. \r
+        * Adds extra optimizations for dynamic (frequently modified) meshes.\r
+        * \r
+        * @param staticVertices whether vertices of this mesh are static or not. Allows for internal optimizations.\r
+        * @param staticIndices whether indices of this mesh are static or not. Allows for internal optimizations.\r
+        * @param maxVertices the maximum number of vertices this mesh can hold\r
+        * @param maxIndices the maximum number of indices this mesh can hold\r
+        * @param attributes the {@link VertexAttributes}. Each vertex attribute defines one property of a vertex such as position,\r
+        *           normal or texture coordinate \r
+        *           \r
+        * @author Jaroslaw Wisniewski <j.wisniewski@appsisle.com>           \r
+        **/\r
+       public Mesh (boolean staticVertices, boolean staticIndices, int maxVertices, int maxIndices, VertexAttributes attributes) {\r
+               if (Gdx.gl20 != null || Gdx.gl11 != null || Mesh.forceVBO) {\r
+                       \r
+                       // buffers do not update when initialized with ..ObjectSubData classes\r
+                       /*if (staticVertices) \r
+                               vertices = new VertexBufferObject(staticVertices, maxVertices, attributes);\r
+                       else \r
+                               vertices = new VertexBufferObjectSubData(staticVertices, maxVertices, attributes);      // when updating vertices - updates buffer instead recreating it\r
+\r
+                       if (staticIndices) \r
+                               indices = new IndexBufferObject(staticIndices, maxIndices);\r
+                       else \r
+                               indices = new IndexBufferObjectSubData(staticIndices, maxIndices);      // when updating indices - updates buffer instead recreating it\r
+                       */\r
+                       \r
+                       vertices = new VertexBufferObject(staticVertices, maxVertices, attributes);\r
+                       indices = new IndexBufferObject(staticIndices, maxIndices);\r
+                       isVertexArray = false;\r
+               } else {\r
+                       vertices = new VertexArray(maxVertices, attributes);\r
+                       indices = new IndexArray(maxIndices);\r
+                       isVertexArray = true;\r
+               }\r
+\r
+               addManagedMesh(Gdx.app, this);\r
+       }\r
+       \r
        /** Creates a new Mesh with the given attributes. This is an expert method with no error checking. Use at your own risk.\r
         * \r
         * @param type the {@link VertexDataType} to be used, VBO or VA.\r
index d64ab56..4a51bb9 100755 (executable)
  ******************************************************************************/
 package com.badlogic.gdx.tests.android;\r
 \r
+import android.util.Log;\r
+\r
 import com.badlogic.gdx.ApplicationAdapter;\r
 import com.badlogic.gdx.ApplicationListener;\r
 import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.android.AndroidWallpaperListener;\r
 import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;\r
 import com.badlogic.gdx.backends.android.AndroidLiveWallpaperService;\r
 import com.badlogic.gdx.graphics.GL10;\r
 import com.badlogic.gdx.math.MathUtils;\r
 import com.badlogic.gdx.tests.Box2DTest;\r
 import com.badlogic.gdx.tests.MeshShaderTest;\r
+import com.badlogic.gdx.tests.SpritePerformanceTest2;\r
 import com.badlogic.gdx.tests.WaterRipples;\r
 \r
 public class LiveWallpaper extends AndroidLiveWallpaperService {\r
+       \r
        @Override\r
-       public ApplicationListener createListener (boolean isPreview) {\r
-               return new MeshShaderTest();\r
-       }\r
-\r
-       @Override\r
-       public AndroidApplicationConfiguration createConfig () {\r
+       public void onCreateApplication () {\r
+               super.onCreateApplication();\r
+               \r
                AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();\r
                config.useGL20 = true;\r
-               return config;\r
+               \r
+               ApplicationListener listener = new MyLiveWallpaperListener();\r
+               initialize(listener, config);\r
        }\r
+       \r
+       // implement AndroidWallpaperListener additionally to ApplicationListener \r
+       // if you want to receive callbacks specific to live wallpapers\r
+       public static class MyLiveWallpaperListener extends MeshShaderTest implements AndroidWallpaperListener {\r
+               \r
+               @Override\r
+               public void offsetChange (float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset,\r
+                       int yPixelOffset) {\r
+                       Log.i("LiveWallpaper test", "offsetChange(xOffset:"+xOffset+" yOffset:"+yOffset+" xOffsetSteep:"+xOffsetStep+" yOffsetStep:"+yOffsetStep+" xPixelOffset:"+xPixelOffset+" yPixelOffset:"+yPixelOffset+")");\r
+               }\r
 \r
-       @Override\r
-       public void offsetChange (ApplicationListener listener, float xOffset, float yOffset, float xOffsetStep, float yOffsetStep,\r
-               int xPixelOffset, int yPixelOffset) {\r
-               Gdx.app.log("LiveWallpaper", "offset changed: " + xOffset + ", " + yOffset);\r
+               @Override\r
+               public void previewStateChange (boolean isPreview) {\r
+                       Log.i("LiveWallpaper test", "previewStateChange(isPreview:"+isPreview+")");\r
+               }\r
        }\r
 }
\ No newline at end of file