Home | History | Annotate | Download | only in android
      1 /*
      2  * Copyright 2010 Mario Zechner (contact (at) badlogicgames.com), Nathan Sweet (admin (at) esotericsoftware.com)
      3  *
      4  * Modified by Elijah Cornell
      5  * 2013.01 Modified by Jaroslaw Wisniewski <j.wisniewski (at) 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 {
     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 
     73 	public AndroidLiveWallpaper (AndroidLiveWallpaperService service) {
     74 		this.service = service;
     75 	}
     76 
     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 
    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 
    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 
    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
    186 	public WindowManager getWindowManager () {
    187 		return service.getWindowManager();
    188 	}
    189 
    190 	public AndroidLiveWallpaperService getService () {
    191 		return service;
    192 	}
    193 
    194 	@Override
    195 	public ApplicationListener getApplicationListener () {
    196 		return listener;
    197 	}
    198 
    199 	@Override
    200 	public void postRunnable (Runnable runnable) {
    201 		synchronized (runnables) {
    202 			runnables.add(runnable);
    203 		}
    204 	}
    205 
    206 	@Override
    207 	public Audio getAudio () {
    208 		return audio;
    209 	}
    210 
    211 	@Override
    212 	public Files getFiles () {
    213 		return files;
    214 	}
    215 
    216 	@Override
    217 	public Graphics getGraphics () {
    218 		return graphics;
    219 	}
    220 
    221 	@Override
    222 	public AndroidInput getInput () {
    223 		return input;
    224 	}
    225 
    226 	@Override
    227 	public Net getNet () {
    228 		return net;
    229 	}
    230 
    231 	@Override
    232 	public ApplicationType getType () {
    233 		return ApplicationType.Android;
    234 	}
    235 
    236 	@Override
    237 	public int getVersion () {
    238 		return android.os.Build.VERSION.SDK_INT;
    239 	}
    240 
    241 	@Override
    242 	public long getJavaHeap () {
    243 		return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
    244 	}
    245 
    246 	@Override
    247 	public long getNativeHeap () {
    248 		return Debug.getNativeHeapAllocatedSize();
    249 	}
    250 
    251 	@Override
    252 	public Preferences getPreferences (String name) {
    253 		return new AndroidPreferences(service.getSharedPreferences(name, Context.MODE_PRIVATE));
    254 	}
    255 
    256 	@Override
    257 	public Clipboard getClipboard () {
    258 		return clipboard;
    259 	}
    260 
    261 	@Override
    262 	public void debug (String tag, String message) {
    263 		if (logLevel >= LOG_DEBUG) {
    264 			Log.d(tag, message);
    265 		}
    266 	}
    267 
    268 	@Override
    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
    276 	public void log (String tag, String message) {
    277 		if (logLevel >= LOG_INFO) Log.i(tag, message);
    278 	}
    279 
    280 	@Override
    281 	public void log (String tag, String message, Throwable exception) {
    282 		if (logLevel >= LOG_INFO) Log.i(tag, message, exception);
    283 	}
    284 
    285 	@Override
    286 	public void error (String tag, String message) {
    287 		if (logLevel >= LOG_ERROR) Log.e(tag, message);
    288 	}
    289 
    290 	@Override
    291 	public void error (String tag, String message, Throwable exception) {
    292 		if (logLevel >= LOG_ERROR) Log.e(tag, message, exception);
    293 	}
    294 
    295 	@Override
    296 	public void setLogLevel (int logLevel) {
    297 		this.logLevel = logLevel;
    298 	}
    299 
    300 	@Override
    301 	public int getLogLevel () {
    302 		return logLevel;
    303 	}
    304 
    305 	@Override
    306 	public void exit () {
    307 		// no-op
    308 	}
    309 
    310 	@Override
    311 	public void addLifecycleListener (LifecycleListener listener) {
    312 		synchronized (lifecycleListeners) {
    313 			lifecycleListeners.add(listener);
    314 		}
    315 	}
    316 
    317 	@Override
    318 	public void removeLifecycleListener (LifecycleListener listener) {
    319 		synchronized (lifecycleListeners) {
    320 			lifecycleListeners.removeValue(listener, true);
    321 		}
    322 	}
    323 
    324 	@Override
    325 	public Context getContext () {
    326 		return service;
    327 	}
    328 
    329 	@Override
    330 	public Array<Runnable> getRunnables () {
    331 		return runnables;
    332 	}
    333 
    334 	@Override
    335 	public Array<Runnable> getExecutedRunnables () {
    336 		return executedRunnables;
    337 	}
    338 
    339 	@Override
    340 	public SnapshotArray<LifecycleListener> getLifecycleListeners () {
    341 		return lifecycleListeners;
    342 	}
    343 
    344 	@Override
    345 	public void startActivity (Intent intent) {
    346 		service.startActivity(intent);
    347 	}
    348 
    349 	@Override
    350 	public Window getApplicationWindow () {
    351 		throw new UnsupportedOperationException();
    352 	}
    353 
    354 	@Override
    355 	public Handler getHandler () {
    356 		throw new UnsupportedOperationException();
    357 	}
    358 
    359 	@Override
    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
    373 	public void useImmersiveMode (boolean b) {
    374 		throw new UnsupportedOperationException();
    375 	}
    376 
    377 }
    378