--- /dev/null
+/*
+ * Copyright (c) 2011 Ben Gruver
+ * All rights reserved.
+ *
+ * You may use this code at your option under the following BSD license
+ * or Apache 2.0 license terms
+ *
+ * [The "BSD license"]
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * [The "Apache 2.0 license"]
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.rbgrn.android.glwallpaperservice;
+
+import android.opengl.GLSurfaceView;
+import android.service.wallpaper.WallpaperService;
+import android.view.SurfaceHolder;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class GLWallpaperService extends WallpaperService {
+ public interface Renderer extends GLSurfaceView.Renderer {
+ }
+
+ public class GLEngine extends WallpaperService.Engine {
+ public final static int RENDERMODE_WHEN_DIRTY = 0;
+ public final static int RENDERMODE_CONTINUOUSLY = 1;
+
+ private Object lock = new Object();
+ private GLSurfaceView mGLSurfaceView = null;
+
+ private int debugFlags;
+ private int renderMode;
+
+ /**
+ * If we don't have a GLSurfaceView yet, then we queue up any operations that are requested, until the
+ * GLSurfaceView is created.
+ *
+ * Initially, we created the glSurfaceView in the GLEngine constructor, and things seemed to work. However,
+ * it turns out a few devices aren't set up to handle the surface related events at this point, and crash.
+ *
+ * This is a work around so that we can delay the creation of the GLSurfaceView until the surface is actually
+ * created, so that the underlying code should be in a state to be able to handle the surface related events
+ * that get fired when GLSurfaceView is created.
+ */
+ private List<Runnable> pendingOperations = new ArrayList<Runnable>();
+
+ public GLEngine() {
+ }
+
+ public void setGLWrapper(final GLSurfaceView.GLWrapper glWrapper) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.setGLWrapper(glWrapper);
+ } else {
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ setGLWrapper(glWrapper);
+ }
+ });
+ }
+ }
+ }
+
+ public void setDebugFlags(final int debugFlags) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.setDebugFlags(debugFlags);
+ } else {
+ this.debugFlags = debugFlags;
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ setDebugFlags(debugFlags);
+ }
+ });
+ }
+ }
+ }
+
+ public int getDebugFlags() {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ return mGLSurfaceView.getDebugFlags();
+ } else {
+ return debugFlags;
+ }
+ }
+ }
+
+ public void setRenderer(final GLSurfaceView.Renderer renderer) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.setRenderer(renderer);
+ if (!isVisible()) {
+ mGLSurfaceView.onPause();
+ }
+ } else {
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ setRenderer(renderer);
+ }
+ });
+ }
+ }
+ }
+
+ public void queueEvent(final Runnable r) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.queueEvent(r);
+ } else {
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ queueEvent(r);
+ }
+ });
+ }
+ }
+ }
+
+ public void setEGLContextFactory(final GLSurfaceView.EGLContextFactory factory) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.setEGLContextFactory(factory);
+ } else {
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ setEGLContextFactory(factory);
+ }
+ });
+ }
+ }
+ }
+
+ public void setEGLWindowSurfaceFactory(final GLSurfaceView.EGLWindowSurfaceFactory factory) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.setEGLWindowSurfaceFactory(factory);
+ } else {
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ setEGLWindowSurfaceFactory(factory);
+ }
+ });
+ }
+ }
+ }
+
+ public void setEGLConfigChooser(final GLSurfaceView.EGLConfigChooser configChooser) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.setEGLConfigChooser(configChooser);
+ } else {
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ setEGLConfigChooser(configChooser);
+ }
+ });
+ }
+ }
+ }
+
+ public void setEGLConfigChooser(final boolean needDepth) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.setEGLConfigChooser(needDepth);
+ } else {
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ setEGLConfigChooser(needDepth);
+ }
+ });
+ }
+ }
+ }
+
+ public void setEGLConfigChooser(final int redSize, final int greenSize, final int blueSize,
+ final int alphaSize, final int depthSize, final int stencilSize) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.setEGLConfigChooser(redSize, greenSize, blueSize,
+ alphaSize, depthSize, stencilSize);
+ } else {
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ setEGLConfigChooser(redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize);
+ }
+ });
+ }
+ }
+ }
+
+ public void setEGLContextClientVersion(final int version) {
+ synchronized (lock) {
+ Method method = null;
+
+ try {
+ //the setEGLContextClientVersion method is first available in api level 8, but we would
+ //like to support compiling against api level 7
+ method = GLSurfaceView.class.getMethod("setEGLContextClientVersion", int.class);
+ } catch (NoSuchMethodException ex) {
+ return;
+ }
+
+ if (mGLSurfaceView != null) {
+ try {
+ method.invoke(mGLSurfaceView, version);
+ } catch (IllegalAccessException ex) {
+ return;
+ } catch (InvocationTargetException ex) {
+ return;
+ }
+ } else {
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ setEGLContextClientVersion(version);
+ }
+ });
+ }
+ }
+ }
+
+ public void setRenderMode(final int renderMode) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.setRenderMode(renderMode);
+ } else {
+ this.renderMode = renderMode;
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ setRenderMode(renderMode);
+ }
+ });
+ }
+ }
+ }
+
+ public int getRenderMode() {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ return mGLSurfaceView.getRenderMode();
+ } else {
+ return renderMode;
+ }
+ }
+ }
+
+ public void requestRender() {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.requestRender();
+ }
+ }
+
+ @Override
+ public void onVisibilityChanged(final boolean visible) {
+ super.onVisibilityChanged(visible);
+
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ if (visible) {
+ mGLSurfaceView.onResume();
+ } else {
+ mGLSurfaceView.onPause();
+ }
+ } else {
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ if (visible) {
+ mGLSurfaceView.onResume();
+ } else {
+ mGLSurfaceView.onPause();
+ }
+ }
+ });
+ }
+ }
+ }
+
+ @Override
+ public void onSurfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.surfaceChanged(holder, format, width, height);
+ } else {
+ pendingOperations.add(new Runnable() {
+ public void run() {
+ onSurfaceChanged(holder, format, width, height);
+ }
+ });
+ }
+ }
+ }
+
+ @Override
+ public void onSurfaceCreated(SurfaceHolder holder) {
+ synchronized (lock) {
+ if (mGLSurfaceView == null) {
+ mGLSurfaceView = new GLSurfaceView(GLWallpaperService.this) {
+ @Override
+ public SurfaceHolder getHolder() {
+ return GLEngine.this.getSurfaceHolder();
+ }
+ };
+ for (Runnable pendingOperation: pendingOperations) {
+ pendingOperation.run();
+ }
+ pendingOperations.clear();
+ }
+ mGLSurfaceView.surfaceCreated(holder);
+ }
+ }
+
+ @Override
+ public void onSurfaceDestroyed(SurfaceHolder holder) {
+ synchronized (lock) {
+ if (mGLSurfaceView != null) {
+ mGLSurfaceView.surfaceDestroyed(holder);
+ }
+ }
+ }
+ }
+}