• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *
7  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
8  * License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
13  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
14  * governing permissions and limitations under the License.
15  */
16 
17 package com.badlogic.gdx.backends.android;
18 
19 import java.lang.reflect.Method;
20 import java.util.Arrays;
21 
22 import android.content.Context;
23 import android.content.Intent;
24 import android.os.Debug;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.util.Log;
28 import android.view.View;
29 import android.view.Window;
30 import android.view.WindowManager;
31 
32 import com.badlogic.gdx.Application;
33 import com.badlogic.gdx.ApplicationListener;
34 import com.badlogic.gdx.Audio;
35 import com.badlogic.gdx.Files;
36 import com.badlogic.gdx.Gdx;
37 import com.badlogic.gdx.Graphics;
38 import com.badlogic.gdx.Input;
39 import com.badlogic.gdx.LifecycleListener;
40 import com.badlogic.gdx.Net;
41 import com.badlogic.gdx.Preferences;
42 import com.badlogic.gdx.backends.android.surfaceview.FillResolutionStrategy;
43 import com.badlogic.gdx.utils.Array;
44 import com.badlogic.gdx.utils.Clipboard;
45 import com.badlogic.gdx.utils.GdxNativesLoader;
46 import com.badlogic.gdx.utils.GdxRuntimeException;
47 import com.badlogic.gdx.utils.SnapshotArray;
48 
49 /** An implementation of the {@link Application} interface to be used with an AndroidLiveWallpaperService. Not directly
50  * constructable, instead the {@link AndroidLiveWallpaperService} will create this class internally.
51  *
52  * @author mzechner */
53 public class AndroidLiveWallpaper implements AndroidApplicationBase {
54 	static {
GdxNativesLoader.load()55 		GdxNativesLoader.load();
56 	}
57 
58 	protected AndroidLiveWallpaperService service;
59 
60 	protected AndroidGraphicsLiveWallpaper graphics;
61 	protected AndroidInput input;
62 	protected AndroidAudio audio;
63 	protected AndroidFiles files;
64 	protected AndroidNet net;
65 	protected AndroidClipboard clipboard;
66 	protected ApplicationListener listener;
67 	protected boolean firstResume = true;
68 	protected final Array<Runnable> runnables = new Array<Runnable>();
69 	protected final Array<Runnable> executedRunnables = new Array<Runnable>();
70 	protected final SnapshotArray<LifecycleListener> lifecycleListeners = new SnapshotArray<LifecycleListener>(LifecycleListener.class);
71 	protected int logLevel = LOG_INFO;
72 
AndroidLiveWallpaper(AndroidLiveWallpaperService service)73 	public AndroidLiveWallpaper (AndroidLiveWallpaperService service) {
74 		this.service = service;
75 	}
76 
initialize(ApplicationListener listener, AndroidApplicationConfiguration config)77 	public void initialize (ApplicationListener listener, AndroidApplicationConfiguration config) {
78 		if (this.getVersion() < MINIMUM_SDK) {
79 			throw new GdxRuntimeException("LibGDX requires Android API Level " + MINIMUM_SDK + " or later.");
80 		}
81 		graphics = new AndroidGraphicsLiveWallpaper(this, config, config.resolutionStrategy == null ? new FillResolutionStrategy()
82 			: config.resolutionStrategy);
83 
84 		// factory in use, but note: AndroidInputFactory causes exceptions when obfuscated: java.lang.RuntimeException: Couldn't
85 		// construct AndroidInput, this should never happen, proguard deletes constructor used only by reflection
86 		input = AndroidInputFactory.newAndroidInput(this, this.getService(), graphics.view, config);
87 		// input = new AndroidInput(this, this.getService(), null, config);
88 
89 		audio = new AndroidAudio(this.getService(), config);
90 
91 		// added initialization of android local storage: /data/data/<app package>/files/
92 		this.getService().getFilesDir(); // workaround for Android bug #10515463
93 		files = new AndroidFiles(this.getService().getAssets(), this.getService().getFilesDir().getAbsolutePath());
94 		net = new AndroidNet(this);
95 		this.listener = listener;
96 		clipboard = new AndroidClipboard(this.getService());
97 
98 		// Unlike activity, fragment and daydream applications there's no need for a specialized audio listener.
99 		// See description in onPause method.
100 
101 		Gdx.app = this;
102 		Gdx.input = input;
103 		Gdx.audio = audio;
104 		Gdx.files = files;
105 		Gdx.graphics = graphics;
106 		Gdx.net = net;
107 	}
108 
onPause()109 	public void onPause () {
110 		if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.TAG, " > AndroidLiveWallpaper - onPause()");
111 
112 		// IMPORTANT!
113 		// jw: graphics.pause is never called, graphics.pause works on most devices but not on all..
114 		// for example on Samsung Galaxy Tab (GT-P6800) on android 4.0.4 invoking graphics.pause causes "Fatal Signal 11"
115 		// near mEglHelper.swap() in GLSurfaceView while processing next onPause event.
116 		// See related issue:
117 		// http://code.google.com/p/libgdx/issues/detail?id=541
118 		// the problem with graphics.pause occurs while using OpenGL 2.0 and original GLSurfaceView while rotating device
119 		// in lwp preview
120 		// in my opinion it is a bug of android not libgdx, even example Cubic live wallpaper from
121 		// Android SDK crashes on affected devices.......... and on some configurations of android emulator too.
122 		//
123 		// My wallpaper was rejected on Samsung Apps because of this issue, so I decided to disable graphics.pause..
124 		// also I moved audio lifecycle methods from AndroidGraphicsLiveWallpaper into this class
125 
126 		// graphics.pause();
127 		// if (AndroidLiveWallpaperService.DEBUG)
128 		// Log.d(AndroidLiveWallpaperService.TAG, " > AndroidLiveWallpaper - onPause() application paused!");
129 		audio.pause();
130 
131 		input.onPause();
132 
133 		if (graphics != null) {
134 			graphics.onPauseGLSurfaceView();
135 		}
136 
137 		if (AndroidLiveWallpaperService.DEBUG) Log.d(AndroidLiveWallpaperService.TAG, " > AndroidLiveWallpaper - onPause() done!");
138 	}
139 
onResume()140 	public void onResume () {
141 		Gdx.app = this;
142 		Gdx.input = input;
143 		Gdx.audio = audio;
144 		Gdx.files = files;
145 		Gdx.graphics = graphics;
146 		Gdx.net = net;
147 
148 		input.onResume();
149 
150 		if (graphics != null) {
151 			graphics.onResumeGLSurfaceView();
152 		}
153 
154 		if (!firstResume) {
155 			audio.resume();
156 			graphics.resume();
157 		} else
158 			firstResume = false;
159 	}
160 
onDestroy()161 	public void onDestroy () {
162 
163 		// it is too late to call graphics.destroy - it needs live gl GLThread and gl context, otherwise it will cause of deadlock
164 		// if (graphics != null) {
165 		// graphics.clearManagedCaches();
166 		// graphics.destroy();
167 		// }
168 
169 		// so we do what we can..
170 		if (graphics != null) {
171 			// not necessary - already called in AndroidLiveWallpaperService.onDeepPauseApplication
172 			// app.graphics.clearManagedCaches();
173 
174 			// kill the GLThread managed by GLSurfaceView
175 			graphics.onDestroyGLSurfaceView();
176 
177 		}
178 
179 		if (audio != null) {
180 			// dispose audio and free native resources, mandatory since graphics.pause is never called in live wallpaper
181 			audio.dispose();
182 		}
183 	}
184 
185 	@Override
getWindowManager()186 	public WindowManager getWindowManager () {
187 		return service.getWindowManager();
188 	}
189 
getService()190 	public AndroidLiveWallpaperService getService () {
191 		return service;
192 	}
193 
194 	@Override
getApplicationListener()195 	public ApplicationListener getApplicationListener () {
196 		return listener;
197 	}
198 
199 	@Override
postRunnable(Runnable runnable)200 	public void postRunnable (Runnable runnable) {
201 		synchronized (runnables) {
202 			runnables.add(runnable);
203 		}
204 	}
205 
206 	@Override
getAudio()207 	public Audio getAudio () {
208 		return audio;
209 	}
210 
211 	@Override
getFiles()212 	public Files getFiles () {
213 		return files;
214 	}
215 
216 	@Override
getGraphics()217 	public Graphics getGraphics () {
218 		return graphics;
219 	}
220 
221 	@Override
getInput()222 	public AndroidInput getInput () {
223 		return input;
224 	}
225 
226 	@Override
getNet()227 	public Net getNet () {
228 		return net;
229 	}
230 
231 	@Override
getType()232 	public ApplicationType getType () {
233 		return ApplicationType.Android;
234 	}
235 
236 	@Override
getVersion()237 	public int getVersion () {
238 		return android.os.Build.VERSION.SDK_INT;
239 	}
240 
241 	@Override
getJavaHeap()242 	public long getJavaHeap () {
243 		return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
244 	}
245 
246 	@Override
getNativeHeap()247 	public long getNativeHeap () {
248 		return Debug.getNativeHeapAllocatedSize();
249 	}
250 
251 	@Override
getPreferences(String name)252 	public Preferences getPreferences (String name) {
253 		return new AndroidPreferences(service.getSharedPreferences(name, Context.MODE_PRIVATE));
254 	}
255 
256 	@Override
getClipboard()257 	public Clipboard getClipboard () {
258 		return clipboard;
259 	}
260 
261 	@Override
debug(String tag, String message)262 	public void debug (String tag, String message) {
263 		if (logLevel >= LOG_DEBUG) {
264 			Log.d(tag, message);
265 		}
266 	}
267 
268 	@Override
debug(String tag, String message, Throwable exception)269 	public void debug (String tag, String message, Throwable exception) {
270 		if (logLevel >= LOG_DEBUG) {
271 			Log.d(tag, message, exception);
272 		}
273 	}
274 
275 	@Override
log(String tag, String message)276 	public void log (String tag, String message) {
277 		if (logLevel >= LOG_INFO) Log.i(tag, message);
278 	}
279 
280 	@Override
log(String tag, String message, Throwable exception)281 	public void log (String tag, String message, Throwable exception) {
282 		if (logLevel >= LOG_INFO) Log.i(tag, message, exception);
283 	}
284 
285 	@Override
error(String tag, String message)286 	public void error (String tag, String message) {
287 		if (logLevel >= LOG_ERROR) Log.e(tag, message);
288 	}
289 
290 	@Override
error(String tag, String message, Throwable exception)291 	public void error (String tag, String message, Throwable exception) {
292 		if (logLevel >= LOG_ERROR) Log.e(tag, message, exception);
293 	}
294 
295 	@Override
setLogLevel(int logLevel)296 	public void setLogLevel (int logLevel) {
297 		this.logLevel = logLevel;
298 	}
299 
300 	@Override
getLogLevel()301 	public int getLogLevel () {
302 		return logLevel;
303 	}
304 
305 	@Override
exit()306 	public void exit () {
307 		// no-op
308 	}
309 
310 	@Override
addLifecycleListener(LifecycleListener listener)311 	public void addLifecycleListener (LifecycleListener listener) {
312 		synchronized (lifecycleListeners) {
313 			lifecycleListeners.add(listener);
314 		}
315 	}
316 
317 	@Override
removeLifecycleListener(LifecycleListener listener)318 	public void removeLifecycleListener (LifecycleListener listener) {
319 		synchronized (lifecycleListeners) {
320 			lifecycleListeners.removeValue(listener, true);
321 		}
322 	}
323 
324 	@Override
getContext()325 	public Context getContext () {
326 		return service;
327 	}
328 
329 	@Override
getRunnables()330 	public Array<Runnable> getRunnables () {
331 		return runnables;
332 	}
333 
334 	@Override
getExecutedRunnables()335 	public Array<Runnable> getExecutedRunnables () {
336 		return executedRunnables;
337 	}
338 
339 	@Override
getLifecycleListeners()340 	public SnapshotArray<LifecycleListener> getLifecycleListeners () {
341 		return lifecycleListeners;
342 	}
343 
344 	@Override
startActivity(Intent intent)345 	public void startActivity (Intent intent) {
346 		service.startActivity(intent);
347 	}
348 
349 	@Override
getApplicationWindow()350 	public Window getApplicationWindow () {
351 		throw new UnsupportedOperationException();
352 	}
353 
354 	@Override
getHandler()355 	public Handler getHandler () {
356 		throw new UnsupportedOperationException();
357 	}
358 
359 	@Override
runOnUiThread(Runnable runnable)360 	public void runOnUiThread (Runnable runnable) {
361 		if (Looper.myLooper() != Looper.getMainLooper()) {
362 			// The current thread is not the UI thread.
363 			// Let's post the runnable to the event queue of the UI thread.
364 			new Handler(Looper.getMainLooper()).post(runnable);
365 		} else {
366 			// The current thread is the UI thread already.
367 			// Let's execute the runnable immediately.
368 			runnable.run();
369 		}
370 	}
371 
372 	@Override
useImmersiveMode(boolean b)373 	public void useImmersiveMode (boolean b) {
374 		throw new UnsupportedOperationException();
375 	}
376 
377 }
378