1 /* 2 * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) 3 * 4 * Modified by Elijah Cornell 5 * 2013.01 Modified by Jaroslaw Wisniewski <j.wisniewski@appsisle.com> 6 * 2014.04 Modified by davebaol 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the 9 * License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" 14 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language 15 * governing permissions and limitations under the License. 16 */ 17 18 package com.badlogic.gdx.backends.android; 19 20 import java.lang.reflect.Method; 21 22 import android.opengl.GLSurfaceView; 23 import android.opengl.GLSurfaceView.EGLConfigChooser; 24 import android.util.Log; 25 import android.view.SurfaceHolder; 26 import android.view.View; 27 28 import com.badlogic.gdx.Gdx; 29 import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20; 30 import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20API18; 31 import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceViewAPI18; 32 import com.badlogic.gdx.backends.android.surfaceview.ResolutionStrategy; 33 import com.badlogic.gdx.utils.GdxRuntimeException; 34 35 /** A subclass of {@link AndroidGraphics} specialized for live wallpaper applications. 36 * 37 * @author mzechner */ 38 public final class AndroidGraphicsLiveWallpaper extends AndroidGraphics { 39 AndroidGraphicsLiveWallpaper(AndroidLiveWallpaper lwp, AndroidApplicationConfiguration config, ResolutionStrategy resolutionStrategy)40 public AndroidGraphicsLiveWallpaper (AndroidLiveWallpaper lwp, AndroidApplicationConfiguration config, 41 ResolutionStrategy resolutionStrategy) { 42 super(lwp, config, resolutionStrategy, false); 43 } 44 45 // jw: I replaced GL..SurfaceViewLW classes with their original counterparts, if it will work 46 // on known devices, on opengl 1.0 and 2.0, and all possible SDK versions.. You can remove 47 // GL..SurfaceViewLW family of classes completely (there is no use for them). 48 49 // -> specific for live wallpapers 50 // jw: synchronized access to current wallpaper surface holder getSurfaceHolder()51 SurfaceHolder getSurfaceHolder () { 52 synchronized (((AndroidLiveWallpaper)app).service.sync) { 53 return ((AndroidLiveWallpaper)app).service.getSurfaceHolder(); 54 } 55 } 56 57 // <- specific for live wallpapers 58 59 // Grabbed from AndroidGraphics superclass and modified to override 60 // getHolder in created GLSurfaceView and GLSurfaceViewAPI18 instances 61 @Override createGLSurfaceView(AndroidApplicationBase application, final ResolutionStrategy resolutionStrategy)62 protected View createGLSurfaceView (AndroidApplicationBase application, final ResolutionStrategy resolutionStrategy) { 63 if (!checkGL20()) throw new GdxRuntimeException("Libgdx requires OpenGL ES 2.0"); 64 65 EGLConfigChooser configChooser = getEglConfigChooser(); 66 int sdkVersion = android.os.Build.VERSION.SDK_INT; 67 if (sdkVersion <= 10 && config.useGLSurfaceView20API18) { 68 GLSurfaceView20API18 view = new GLSurfaceView20API18(application.getContext(), resolutionStrategy) { 69 @Override 70 public SurfaceHolder getHolder () { 71 return getSurfaceHolder(); 72 } 73 74 // This method is invoked via reflection by AndroidLiveWallpaper.onDestroy() 75 public void onDestroy () { 76 onDetachedFromWindow(); // calls GLSurfaceView.mGLThread.requestExitAndWait(); 77 } 78 }; 79 if (configChooser != null) 80 view.setEGLConfigChooser(configChooser); 81 else 82 view.setEGLConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil); 83 view.setRenderer(this); 84 return view; 85 } 86 else { 87 GLSurfaceView20 view = new GLSurfaceView20(application.getContext(), resolutionStrategy) { 88 @Override 89 public SurfaceHolder getHolder () { 90 return getSurfaceHolder(); 91 } 92 93 // This method is invoked via reflection by AndroidLiveWallpaper.onDestroy() 94 public void onDestroy () { 95 onDetachedFromWindow(); // calls GLSurfaceView.mGLThread.requestExitAndWait(); 96 } 97 }; 98 99 if (configChooser != null) 100 view.setEGLConfigChooser(configChooser); 101 else 102 view.setEGLConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil); 103 view.setRenderer(this); 104 return view; 105 } 106 } 107 108 // kill the GLThread managed by GLSurfaceView (only for GLSurfaceView because GLSurffaceViewCupcake stops thread in 109 // onPause events - which is not as easy and safe for GLSurfaceView) onDestroyGLSurfaceView()110 public void onDestroyGLSurfaceView () { 111 if (view != null) { 112 if (view instanceof GLSurfaceView || view instanceof GLSurfaceViewAPI18) { 113 try { 114 // onDestroy redirects to onDetachedFromWindow - which stops GLThread by calling mGLThread.requestExitAndWait() 115 view.getClass().getMethod("onDestroy").invoke(view); 116 if (AndroidLiveWallpaperService.DEBUG) 117 Log.d(AndroidLiveWallpaperService.TAG, 118 " > AndroidLiveWallpaper - onDestroy() stopped GLThread managed by GLSurfaceView"); 119 } catch (Throwable t) { 120 // error while scheduling exit of GLThread, GLThread will remain live and wallpaper service 121 // wouldn't be able to shutdown completely 122 Log.e(AndroidLiveWallpaperService.TAG, 123 "failed to destroy GLSurfaceView's thread! GLSurfaceView.onDetachedFromWindow impl changed since API lvl 16!"); 124 t.printStackTrace(); 125 } 126 } 127 } 128 } 129 130 @Override resume()131 void resume () { 132 synchronized (synch) { 133 running = true; 134 resume = true; 135 136 // by jw: added synchronization, there was nothing before 137 while (resume) { 138 try { 139 requestRendering(); 140 synch.wait(); 141 } catch (InterruptedException ignored) { 142 Gdx.app.log("AndroidGraphics", "waiting for resume synchronization failed!"); 143 } 144 } 145 } 146 } 147 148 @Override onDrawFrame(javax.microedition.khronos.opengles.GL10 gl)149 public void onDrawFrame (javax.microedition.khronos.opengles.GL10 gl) { 150 long time = System.nanoTime(); 151 deltaTime = (time - lastFrameTime) / 1000000000.0f; 152 lastFrameTime = time; 153 154 // After pause deltaTime can have somewhat huge value that destabilizes the mean, so let's cut it off 155 if (!resume) { 156 mean.addValue(deltaTime); 157 } else { 158 deltaTime = 0; 159 } 160 161 boolean lrunning = false; 162 boolean lpause = false; 163 boolean ldestroy = false; 164 boolean lresume = false; 165 166 synchronized (synch) { 167 lrunning = running; 168 lpause = pause; 169 ldestroy = destroy; 170 lresume = resume; 171 172 if (resume) { 173 resume = false; 174 // by jw: originally was not synchronized 175 synch.notifyAll(); 176 } 177 178 if (pause) { 179 pause = false; 180 synch.notifyAll(); 181 } 182 183 if (destroy) { 184 destroy = false; 185 synch.notifyAll(); 186 } 187 } 188 189 if (lresume) { 190 // ((AndroidAudio)app.getAudio()).resume(); // jw: moved to AndroidLiveWallpaper.onResume 191 app.getApplicationListener().resume(); 192 Gdx.app.log("AndroidGraphics", "resumed"); 193 } 194 195 // HACK: added null check to handle set wallpaper from preview null 196 // error in renderer 197 // jw: this hack is not working always, renderer ends with error for some devices - because of uninitialized gl context 198 // jw: now it shouldn't be necessary - after wallpaper backend refactoring:) 199 if (lrunning) { 200 201 // jw: changed 202 synchronized (app.getRunnables()) { 203 app.getExecutedRunnables().clear(); 204 app.getExecutedRunnables().addAll(app.getRunnables()); 205 app.getRunnables().clear(); 206 207 for (int i = 0; i < app.getExecutedRunnables().size; i++) { 208 try { 209 app.getExecutedRunnables().get(i).run(); 210 } catch (Throwable t) { 211 t.printStackTrace(); 212 } 213 } 214 } 215 /* 216 * synchronized (app.runnables) { for (int i = 0; i < app.runnables.size; i++) { app.runnables.get(i).run(); } 217 * app.runnables.clear(); } 218 */ 219 220 app.getInput().processEvents(); 221 frameId++; 222 app.getApplicationListener().render(); 223 } 224 225 // jw: never called on lvp, why? see description in AndroidLiveWallpaper.onPause 226 if (lpause) { 227 app.getApplicationListener().pause(); 228 // ((AndroidAudio)app.getAudio()).pause(); jw: moved to AndroidLiveWallpaper.onPause 229 Gdx.app.log("AndroidGraphics", "paused"); 230 } 231 232 // jw: never called on lwp, why? see description in AndroidLiveWallpaper.onPause 233 if (ldestroy) { 234 app.getApplicationListener().dispose(); 235 // ((AndroidAudio)app.getAudio()).dispose(); jw: moved to AndroidLiveWallpaper.onDestroy 236 Gdx.app.log("AndroidGraphics", "destroyed"); 237 } 238 239 if (time - frameStart > 1000000000) { 240 fps = frames; 241 frames = 0; 242 frameStart = time; 243 } 244 frames++; 245 } 246 247 @Override logManagedCachesStatus()248 protected void logManagedCachesStatus() { 249 // to prevent creating too many string buffers in live wallpapers 250 if (AndroidLiveWallpaperService.DEBUG) { 251 super.logManagedCachesStatus(); 252 } 253 } 254 } 255