2 * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com)
\r
4 * Modified by Elijah Cornell
\r
5 * 2013.01 Modified by Jaroslaw Wisniewski <j.wisniewski@appsisle.com>
\r
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
\r
8 * License. You may obtain a copy of the License at
\r
10 * http://www.apache.org/licenses/LICENSE-2.0
\r
12 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
\r
13 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
\r
14 * governing permissions and limitations under the License.
\r
18 package com.badlogic.gdx.backends.android;
\r
20 import java.lang.reflect.Method;
\r
21 import java.util.ArrayList;
\r
22 import java.util.List;
\r
24 import android.app.Activity;
\r
25 import android.content.Context;
\r
26 import android.content.res.Configuration;
\r
27 import android.opengl.GLSurfaceView;
\r
28 import android.os.Build;
\r
29 import android.os.Bundle;
\r
30 import android.os.Debug;
\r
31 import android.os.Handler;
\r
32 import android.service.wallpaper.WallpaperService;
\r
33 import android.service.wallpaper.WallpaperService.Engine;
\r
34 import android.util.Log;
\r
35 import android.view.View;
\r
36 import android.view.WindowManager;
\r
37 import android.widget.FrameLayout.LayoutParams;
\r
39 import com.badlogic.gdx.Application;
\r
40 import com.badlogic.gdx.ApplicationListener;
\r
41 import com.badlogic.gdx.Audio;
\r
42 import com.badlogic.gdx.Files;
\r
43 import com.badlogic.gdx.Gdx;
\r
44 import com.badlogic.gdx.Graphics;
\r
45 import com.badlogic.gdx.Input;
\r
46 import com.badlogic.gdx.LifecycleListener;
\r
47 import com.badlogic.gdx.Net;
\r
48 import com.badlogic.gdx.Preferences;
\r
49 import com.badlogic.gdx.Application.ApplicationType;
\r
50 import com.badlogic.gdx.backends.android.surfaceview.FillResolutionStrategy;
\r
51 import com.badlogic.gdx.backends.android.surfaceview.GLBaseSurfaceViewLW;
\r
52 import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20;
\r
53 import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20LW;
\r
54 import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceViewCupcake;
\r
55 import com.badlogic.gdx.graphics.GL10;
\r
56 import com.badlogic.gdx.graphics.GL11;
\r
57 import com.badlogic.gdx.utils.Array;
\r
58 import com.badlogic.gdx.utils.Clipboard;
\r
59 import com.badlogic.gdx.utils.GdxNativesLoader;
\r
62 * An implementation of the {@link Application} interface to be used with an
\r
63 * AndroidLiveWallpaperService. Not directly constructable, instead the
\r
64 * {@link AndroidLiveWallpaperService} will create this class internally.
\r
68 public class AndroidLiveWallpaper implements Application {
\r
70 GdxNativesLoader.load();
\r
73 protected AndroidLiveWallpaperService service;
\r
75 protected AndroidGraphicsLiveWallpaper graphics;
\r
76 protected AndroidInput input;
\r
77 protected AndroidAudio audio;
\r
78 protected AndroidFiles files;
\r
79 protected AndroidNet net;
\r
80 protected ApplicationListener listener;
\r
81 protected boolean firstResume = true;
\r
82 protected final Array<Runnable> runnables = new Array<Runnable>();
\r
83 protected final Array<Runnable> executedRunnables = new Array<Runnable>();
\r
84 protected final Array<LifecycleListener> lifecycleListeners = new Array<LifecycleListener>();
\r
85 protected int logLevel = LOG_INFO;
\r
87 public AndroidLiveWallpaper(AndroidLiveWallpaperService service) {
\r
88 this.service = service;
\r
91 public void initialize(ApplicationListener listener, AndroidApplicationConfiguration config) {
\r
92 graphics = new AndroidGraphicsLiveWallpaper(this, config, config.resolutionStrategy==null?new FillResolutionStrategy():config.resolutionStrategy);
\r
94 // 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
95 input = AndroidInputFactory.newAndroidInput(this, this.getService(), graphics.view, config);
\r
96 //input = new AndroidInput(this, this.getService(), null, config);
\r
98 audio = new AndroidAudio(this.getService(), config);
\r
100 // added initialization of android local storage: /data/data/<app package>/files/
\r
101 files = new AndroidFiles(this.getService().getAssets(), this.getService().getFilesDir().getAbsolutePath());
\r
103 this.listener = listener;
\r
109 Gdx.graphics = graphics;
\r
112 public void onPause() {
\r
113 if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.TAG, " > AndroidLiveWallpaper - onPause()");
\r
116 // jw: graphics.pause is never called, graphics.pause works on most devices but not on all..
\r
117 // for example on Samsung Galaxy Tab (GT-P6800) on android 4.0.4 invoking graphics.pause causes "Fatal Signal 11"
\r
118 // near mEglHelper.swap() in GLSurfaceView while processing next onPause event.
\r
119 // See related issue:
\r
120 // http://code.google.com/p/libgdx/issues/detail?id=541
\r
121 // the problem with graphics.pause occurs while using OpenGL 2.0 and original GLSurfaceView while rotating device in lwp preview
\r
122 // in my opinion it is a bug of android not libgdx, even example Cubic live wallpaper from
\r
123 // Android SDK crashes on affected devices.......... and on some configurations of android emulator too.
\r
125 // My wallpaper was rejected on Samsung Apps because of this issue, so I decided to disable graphics.pause..
\r
126 // also I moved audio lifecycle methods from AndroidGraphicsLiveWallpaper into this class
\r
128 //graphics.pause();
\r
129 //if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.TAG, " > AndroidLiveWallpaper - onPause() application paused!");
\r
132 input.unregisterSensorListeners();
\r
133 // erase pointer ids. this sucks donkeyballs...
\r
134 int[] realId = input.realId;
\r
135 for (int i = 0; i < realId.length; i++)
\r
137 // erase touched state. this also sucks donkeyballs...
\r
138 boolean[] touched = input.touched;
\r
139 for (int i = 0; i < touched.length; i++)
\r
140 touched[i] = false;
\r
142 if (graphics != null && graphics.view != null) {
\r
143 if (graphics.view instanceof GLSurfaceViewCupcake) ((GLSurfaceViewCupcake)graphics.view).onPause();
\r
144 else if (graphics.view instanceof android.opengl.GLSurfaceView) ((android.opengl.GLSurfaceView)graphics.view).onPause();
\r
145 else throw new RuntimeException("unimplemented");
\r
148 if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.TAG, " > AndroidLiveWallpaper - onPause() done!");
\r
151 public void onResume() {
\r
156 Gdx.graphics = graphics;
\r
158 input.registerSensorListeners();
\r
160 // FIXME restore conditional execution if lifecycle errors will occur when GLSurfaceView used.
\r
161 // GLSurfaceView is guaranteed to work with this condition on, but GLSurfaceViewCupcake requires it off,
\r
162 // so I disabled it.
\r
163 //if (!firstResume) // mentioned condition
\r
164 if (graphics != null && graphics.view != null) {
\r
165 if (graphics.view instanceof GLSurfaceViewCupcake) ((GLSurfaceViewCupcake)graphics.view).onResume();
\r
166 else if (graphics.view instanceof android.opengl.GLSurfaceView) ((android.opengl.GLSurfaceView)graphics.view).onResume();
\r
167 else throw new RuntimeException("unimplemented");
\r
176 firstResume = false;
\r
179 public void onDestroy() {
\r
181 // it is too late to call graphics.destroy - it needs live gl GLThread and gl context, otherwise it will cause of deadlock
\r
182 //if (graphics != null) {
\r
183 // graphics.clearManagedCaches();
\r
184 // graphics.destroy();
\r
187 // so we do what we can..
\r
188 if (graphics != null)
\r
190 // not necessary - already called in AndroidLiveWallpaperService.onDeepPauseApplication
\r
191 // app.graphics.clearManagedCaches();
\r
193 // 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
194 if (graphics.view != null && (graphics.view instanceof GLSurfaceView))
\r
196 GLSurfaceView glSurfaceView = (GLSurfaceView)graphics.view;
\r
198 Method method = null;
\r
199 for (Method m : glSurfaceView.getClass().getMethods())
\r
201 if (m.getName().equals("onDestroy")) // implemented in AndroidGraphicsLiveWallpaper, redirects to onDetachedFromWindow - which stops GLThread by calling mGLThread.requestExitAndWait()
\r
208 if (method != null)
\r
210 method.invoke(glSurfaceView);
\r
211 if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.TAG, " > AndroidLiveWallpaper - onDestroy() stopped GLThread managed by GLSurfaceView");
\r
214 throw new Exception("method not found!");
\r
216 catch (Throwable t)
\r
218 // error while scheduling exit of GLThread, GLThread will remain live and wallpaper service wouldn't be able to shutdown completely
\r
219 Log.e(AndroidLiveWallpaperService.TAG, "failed to destroy GLSurfaceView's thread! GLSurfaceView.onDetachedFromWindow impl changed since API lvl 16!");
\r
220 t.printStackTrace();
\r
227 // dispose audio and free native resources, mandatory since graphics.pause is never called in live wallpaper
\r
232 public WindowManager getWindowManager() {
\r
233 return service.getWindowManager();
\r
236 public AndroidLiveWallpaperService getService() {
\r
240 public ApplicationListener getListener() {
\r
245 public void postRunnable (Runnable runnable) {
\r
246 synchronized(runnables) {
\r
247 runnables.add(runnable);
\r
252 public Audio getAudio () {
\r
257 public Files getFiles () {
\r
262 public Graphics getGraphics () {
\r
267 public Input getInput () {
\r
272 public Net getNet () {
\r
277 public ApplicationType getType () {
\r
278 return ApplicationType.Android;
\r
282 public int getVersion () {
\r
283 return Integer.parseInt(android.os.Build.VERSION.SDK);
\r
287 public long getJavaHeap () {
\r
288 return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
\r
292 public long getNativeHeap () {
\r
293 return Debug.getNativeHeapAllocatedSize();
\r
297 public Preferences getPreferences (String name) {
\r
298 return new AndroidPreferences(service.getSharedPreferences(name, Context.MODE_PRIVATE));
\r
301 AndroidClipboard clipboard;
\r
304 public Clipboard getClipboard() {
\r
305 if (clipboard == null) {
\r
306 clipboard = new AndroidClipboard(service);
\r
312 public void debug (String tag, String message) {
\r
313 if (logLevel >= LOG_DEBUG) {
\r
314 Log.d(tag, message);
\r
319 public void debug (String tag, String message, Throwable exception) {
\r
320 if (logLevel >= LOG_DEBUG) {
\r
321 Log.d(tag, message, exception);
\r
326 public void log (String tag, String message) {
\r
327 if (logLevel >= LOG_INFO) Log.i(tag, message);
\r
331 public void log (String tag, String message, Exception exception) {
\r
332 if (logLevel >= LOG_INFO) Log.i(tag, message, exception);
\r
336 public void error (String tag, String message) {
\r
337 if (logLevel >= LOG_ERROR) Log.e(tag, message);
\r
341 public void error (String tag, String message, Throwable exception) {
\r
342 if (logLevel >= LOG_ERROR) Log.e(tag, message, exception);
\r
346 public void setLogLevel (int logLevel) {
\r
347 this.logLevel = logLevel;
\r
351 public void exit () {
\r
356 public void addLifecycleListener (LifecycleListener listener) {
\r
357 synchronized(lifecycleListeners) {
\r
358 lifecycleListeners.add(listener);
\r
363 public void removeLifecycleListener (LifecycleListener listener) {
\r
364 synchronized(lifecycleListeners) {
\r
365 lifecycleListeners.removeValue(listener, true);
\r
370 public ApplicationListener getApplicationListener () {
\r