1 package com.jme3.app;
\r
3 import java.util.logging.Handler;
\r
4 import java.util.logging.Level;
\r
5 import java.util.logging.Logger;
\r
6 import java.util.logging.SimpleFormatter;
\r
7 import com.jme3.util.JmeFormatter;
\r
8 import android.app.Activity;
\r
9 import android.app.AlertDialog;
\r
10 import android.content.DialogInterface;
\r
11 import android.content.pm.ActivityInfo;
\r
12 import android.opengl.GLSurfaceView;
\r
13 import android.os.Bundle;
\r
14 import android.view.Display;
\r
15 import android.view.SurfaceView;
\r
16 import android.view.View;
\r
17 import android.view.Window;
\r
18 import android.view.WindowManager;
\r
19 import android.widget.TextView;
\r
21 import com.jme3.app.Application;
\r
22 import com.jme3.input.TouchInput;
\r
23 import com.jme3.input.android.AndroidInput;
\r
24 import com.jme3.input.controls.TouchListener;
\r
25 import com.jme3.input.controls.TouchTrigger;
\r
26 import com.jme3.input.event.TouchEvent;
\r
27 import com.jme3.system.AppSettings;
\r
28 import com.jme3.system.JmeSystem;
\r
29 import com.jme3.system.android.OGLESContext;
\r
30 import com.jme3.system.android.AndroidConfigChooser.ConfigType;
\r
33 * <code>AndroidHarness</code> wraps a jme application object and runs it on Android
\r
37 public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener {
\r
39 protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName());
\r
41 * The application class to start
\r
43 protected String appClass = "jme3test.android.Test";
\r
45 * The jme3 application object
\r
47 protected Application app = null;
\r
49 * ConfigType.FASTEST is RGB565, GLSurfaceView default
\r
50 * ConfigType.BEST is RGBA8888 or better if supported by the hardware
\r
52 protected ConfigType eglConfigType = ConfigType.FASTEST;
\r
54 * If true all valid and not valid egl configs are logged
\r
56 protected boolean eglConfigVerboseLogging = false;
\r
58 * If true MouseEvents are generated from TouchEvents
\r
60 protected boolean mouseEventsEnabled = true;
\r
64 protected boolean mouseEventsInvertX = true;
\r
68 protected boolean mouseEventsInvertY = true;
\r
70 * Title of the exit dialog, default is "Do you want to exit?"
\r
72 protected String exitDialogTitle = "Do you want to exit?";
\r
74 * Message of the exit dialog, default is "Use your home key to bring this app into the background or exit to terminate it."
\r
76 protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it.";
\r
79 * Set the screen window size
\r
80 * if screenFullSize is true, then the notification bar and title bar are
\r
81 * removed and the screen covers the entire display
\r
82 * if screenFullSize is false, then the notification bar remains visible
\r
83 * if screenShowTitle is true while screenFullScreen is false, then the
\r
84 * title bar is also displayed under the notification bar
\r
86 protected boolean screenFullScreen = true;
\r
89 * if screenShowTitle is true while screenFullScreen is false, then the
\r
90 * title bar is also displayed under the notification bar
\r
92 protected boolean screenShowTitle = true;
\r
95 * Set the screen orientation, default is SENSOR
\r
96 * ActivityInfo.SCREEN_ORIENTATION_* constants
\r
97 * package android.content.pm.ActivityInfo
\r
99 * SCREEN_ORIENTATION_UNSPECIFIED
\r
100 * SCREEN_ORIENTATION_LANDSCAPE
\r
101 * SCREEN_ORIENTATION_PORTRAIT
\r
102 * SCREEN_ORIENTATION_USER
\r
103 * SCREEN_ORIENTATION_BEHIND
\r
104 * SCREEN_ORIENTATION_SENSOR (default)
\r
105 * SCREEN_ORIENTATION_NOSENSOR
\r
107 protected int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
\r
108 protected OGLESContext ctx;
\r
109 protected GLSurfaceView view = null;
\r
110 protected boolean isGLThreadPaused = true;
\r
111 final private String ESCAPE_EVENT = "TouchEscape";
\r
114 public void onCreate(Bundle savedInstanceState) {
\r
115 super.onCreate(savedInstanceState);
\r
117 Logger log = logger;
\r
118 boolean bIsLogFormatSet = false;
\r
120 if (log.getHandlers().length == 0) {
\r
121 log = logger.getParent();
\r
123 for (Handler h : log.getHandlers()) {
\r
124 //h.setFormatter(new SimpleFormatter());
\r
125 h.setFormatter(new JmeFormatter());
\r
126 bIsLogFormatSet = true;
\r
130 } while (log != null && !bIsLogFormatSet);
\r
132 JmeSystem.setResources(getResources());
\r
133 JmeSystem.setActivity(this);
\r
135 if (screenFullScreen) {
\r
136 requestWindowFeature(Window.FEATURE_NO_TITLE);
\r
137 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
\r
138 WindowManager.LayoutParams.FLAG_FULLSCREEN);
\r
140 if (!screenShowTitle) {
\r
141 requestWindowFeature(Window.FEATURE_NO_TITLE);
\r
145 setRequestedOrientation(screenOrientation);
\r
148 AppSettings settings = new AppSettings(true);
\r
150 // Create the input class
\r
151 AndroidInput input = new AndroidInput(this);
\r
152 input.setMouseEventsInvertX(mouseEventsInvertX);
\r
153 input.setMouseEventsInvertY(mouseEventsInvertY);
\r
154 input.setMouseEventsEnabled(mouseEventsEnabled);
\r
156 // Create application instance
\r
159 @SuppressWarnings("unchecked")
\r
160 Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);
\r
161 app = clazz.newInstance();
\r
164 app.setSettings(settings);
\r
166 ctx = (OGLESContext) app.getContext();
\r
167 view = ctx.createView(input, eglConfigType, eglConfigVerboseLogging);
\r
168 setContentView(view);
\r
170 // Set the screen reolution
\r
171 WindowManager wind = this.getWindowManager();
\r
172 Display disp = wind.getDefaultDisplay();
\r
173 ctx.getSettings().setResolution(disp.getWidth(), disp.getHeight());
\r
175 AppSettings s = ctx.getSettings();
\r
176 logger.log(Level.INFO, "Settings: Width {0} Height {1}", new Object[]{s.getWidth(), s.getHeight()});
\r
177 } catch (Exception ex) {
\r
178 handleError("Class " + appClass + " init failed", ex);
\r
179 setContentView(new TextView(this));
\r
184 protected void onRestart() {
\r
189 logger.info("onRestart");
\r
193 protected void onStart() {
\r
195 logger.info("onStart");
\r
199 protected void onResume() {
\r
201 if (view != null) {
\r
204 isGLThreadPaused = false;
\r
205 logger.info("onResume");
\r
209 protected void onPause() {
\r
211 if (view != null) {
\r
214 isGLThreadPaused = true;
\r
215 logger.info("onPause");
\r
219 protected void onStop() {
\r
221 logger.info("onStop");
\r
225 protected void onDestroy() {
\r
227 app.stop(!isGLThreadPaused);
\r
230 logger.info("onDestroy");
\r
233 public Application getJmeApplication() {
\r
238 * Called when an error has occured. This is typically
\r
239 * invoked when an uncaught exception is thrown in the render thread.
\r
240 * @param errorMsg The error message, if any, or null.
\r
241 * @param t Throwable object, or null.
\r
243 public void handleError(final String errorMsg, final Throwable t) {
\r
244 String sTrace = "";
\r
245 if (t != null && t.getStackTrace() != null) {
\r
246 for (StackTraceElement ste : t.getStackTrace()) {
\r
247 sTrace += "\tat " + ste.getClassName() + "." + ste.getMethodName() + "(";
\r
248 if (ste.isNativeMethod()){
\r
249 sTrace += "Native";
\r
251 sTrace += ste.getLineNumber();
\r
257 final String stackTrace = sTrace;
\r
259 logger.log(Level.SEVERE, t != null ? t.toString() : "OpenGL Exception");
\r
260 logger.log(Level.SEVERE, "{0}{1}", new Object[]{errorMsg != null ? errorMsg + ": " : "", stackTrace});
\r
262 this.runOnUiThread(new Runnable() {
\r
264 public void run() {
\r
265 AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
\r
266 .setTitle(t != null ? (t.getMessage() != null ? (t.getMessage() + ": " + t.getClass().getName()) : t.getClass().getName()) : "OpenGL Exception").setPositiveButton("Kill", AndroidHarness.this).setMessage((errorMsg != null ? errorMsg + ": " : "") + stackTrace).create();
\r
274 * Called by the android alert dialog, terminate the activity and OpenGL rendering
\r
276 * @param whichButton
\r
278 public void onClick(DialogInterface dialog, int whichButton) {
\r
279 if (whichButton != -2) {
\r
288 * Gets called by the InputManager on all touch/drag/scale events
\r
291 public void onTouch(String name, TouchEvent evt, float tpf) {
\r
292 if (name.equals(ESCAPE_EVENT)) {
\r
293 switch (evt.getType()) {
\r
295 this.runOnUiThread(new Runnable() {
\r
297 public void run() {
\r
298 AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
\r
299 .setTitle(exitDialogTitle).setPositiveButton("Yes", AndroidHarness.this).setNegativeButton("No", AndroidHarness.this).setMessage(exitDialogMessage).create();
\r