From 521d1daccfc1ee60d6db0ae24780773e54cafe0f Mon Sep 17 00:00:00 2001 From: Aaron Culliney Date: Sun, 1 Nov 2015 10:14:40 -0800 Subject: [PATCH] Refactor app startup - Make sure crash checking is performed as early as possible - Minimize calling complex native functions until after splash screen is dimissed - Do not attempt to setup/resume OpenGL until after splash screen is dimissed --- .../org/deadc0de/apple2ix/Apple2Activity.java | 158 +++++++----------- .../org/deadc0de/apple2ix/Apple2View.java | 74 +++++++- Android/jni/jnihooks.c | 11 +- 3 files changed, 130 insertions(+), 113 deletions(-) diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java index 9d5ccb83..a031a459 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java @@ -15,7 +15,6 @@ import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; -import android.graphics.Rect; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; @@ -25,7 +24,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.Toast; @@ -51,9 +49,6 @@ public class Apple2Activity extends Activity { private AtomicBoolean mPausing = new AtomicBoolean(false); - private int mWidth = 0; - private int mHeight = 0; - private float[] mXCoords = new float[MAX_FINGERS]; private float[] mYCoords = new float[MAX_FINGERS]; @@ -88,17 +83,11 @@ public class Apple2Activity extends Activity { public final static long NATIVE_TOUCH_ASCII_MASK = 0xFF00L; public final static long NATIVE_TOUCH_SCANCODE_MASK = 0x00FFL; - private native void nativeOnCreate(String dataDir, int sampleRate, int monoBufferSize, int stereoBufferSize); - - private native void nativeGraphicsInitialized(int width, int height); - - private native void nativeGraphicsChanged(int width, int height); - private native void nativeOnKeyDown(int keyCode, int metaState); private native void nativeOnKeyUp(int keyCode, int metaState); - private native void nativeEmulationResume(); + public native void nativeEmulationResume(); public native void nativeEmulationPause(); @@ -108,8 +97,6 @@ public class Apple2Activity extends Activity { public native void nativeReboot(); - public native void nativeRender(); - public native void nativeChooseDisk(String path, boolean driveA, boolean readOnly); public native void nativeEjectDisk(boolean driveA); @@ -135,55 +122,36 @@ public class Apple2Activity extends Activity { Log.e(TAG, "onCreate()"); + // placeholder view on initial launch + if (mView == null) { + setContentView(new View(this)); + } + Apple2CrashHandler.getInstance().initializeAndSetCustomExceptionHandler(this); if (sNativeBarfed) { Log.e(TAG, "NATIVE BARFED...", sNativeBarfedThrowable); - View view = new View(this); - setContentView(view); return; } - // run first-time initializations + showSplashScreen(); + Apple2CrashHandler.getInstance().checkForCrashes(Apple2Activity.this); + + // first-time initializations #1 if (!Apple2Preferences.FIRST_TIME_CONFIGURED.booleanValue(this)) { Apple2DisksMenu.firstTime(this); - Apple2Preferences.KeypadPreset.IJKM_SPACE.apply(this); } - Apple2Preferences.FIRST_TIME_CONFIGURED.saveBoolean(this, true); - // get device audio parameters for native OpenSLES - int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(this); - int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/false); - int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/true); - Log.d(TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize); - - String dataDir = Apple2DisksMenu.getDataDir(this); - nativeOnCreate(dataDir, sampleRate, monoBufferSize, stereoBufferSize); - - // NOTE: load preferences after nativeOnCreate ... native CPU thread should still be paused - Apple2Preferences.loadPreferences(this); - - mView = new Apple2View(this); - setContentView(mView); - - // Another Android Annoyance ... - // Even though we no longer use the system soft keyboard (which would definitely trigger width/height changes to our OpenGL canvas), - // we still need to listen to dimension changes, because it seems on some janky devices you have an incorrect width/height set when - // the initial OpenGL onSurfaceChanged() callback occurs. For now, include this defensive coding... - mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - public void onGlobalLayout() { - Rect rect = new Rect(); - mView.getWindowVisibleDisplayFrame(rect); - int h = rect.height(); - int w = rect.width(); - if (w < h) { - // assure landscape dimensions - final int w_ = w; - w = h; - h = w_; - } - nativeGraphicsChanged(w, h); + Intent intent = getIntent(); + String path = null; + if (intent != null) { + Uri data = intent.getData(); + if (data != null) { + path = data.getPath(); } - }); + } + if (path != null && Apple2DisksMenu.hasDiskExtension(path)) { + handleInsertDiskIntent(path); + } } @Override @@ -195,7 +163,7 @@ public class Apple2Activity extends Activity { } Log.d(TAG, "onResume()"); - mView.onResume(); + showSplashScreen(); } @Override @@ -211,7 +179,9 @@ public class Apple2Activity extends Activity { } Log.d(TAG, "onPause()"); - mView.onPause(); + if (mView != null) { + mView.onPause(); + } // Apparently not good to leave popup/dialog windows showing when backgrounding. // Dismiss these popups to avoid android.view.WindowLeaked issues @@ -354,40 +324,6 @@ public class Apple2Activity extends Activity { return super.onTouchEvent(event); } - void graphicsInitialized(int w, int h) { - Log.v(TAG, "graphicsInitialized(" + w + ", " + h + ")"); - - if (mMainMenu == null) { - mMainMenu = new Apple2MainMenu(this, mView); - } - - if (w < h) { - // assure landscape dimensions - final int w_ = w; - w = h; - h = w_; - } - - mWidth = w; - mHeight = h; - - nativeGraphicsInitialized(w, h); - - showSplashScreen(); - - Intent intent = getIntent(); - String path = null; - if (intent != null) { - Uri data = intent.getData(); - if (data != null) { - path = data.getPath(); - } - } - if (path != null && Apple2DisksMenu.hasDiskExtension(path)) { - handleInsertDiskIntent(path); - } - } - public void showMainMenu() { if (mMainMenu != null) { Apple2SettingsMenu settingsMenu = mMainMenu.getSettingsMenu(); @@ -456,18 +392,28 @@ public class Apple2Activity extends Activity { }); } - private synchronized void showSplashScreen() { + private void showSplashScreen() { if (mSplashScreen != null) { return; } mSplashScreen = new Apple2SplashScreen(Apple2Activity.this); - runOnUiThread(new Runnable() { - @Override - public void run() { - mSplashScreen.show(); - Apple2CrashHandler.getInstance().checkForCrashes(Apple2Activity.this); - } - }); + mSplashScreen.show(); + } + + private void setupGLView() { + + boolean glViewFirstTime = false; + if (mView == null) { + glViewFirstTime = true; + mView = new Apple2View(this); + mMainMenu = new Apple2MainMenu(this, mView); + } + + setContentView(mView); + + if (!glViewFirstTime) { + mView.onResume(); + } } public void registerAndShowDialog(AlertDialog dialog) { @@ -479,7 +425,7 @@ public class Apple2Activity extends Activity { mMenuStack.add(apple2MenuView); View menuView = apple2MenuView.getView(); nativeEmulationPause(); - addContentView(menuView, new FrameLayout.LayoutParams(mWidth, mHeight)); + addContentView(menuView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); } public synchronized Apple2MenuView popApple2View() { @@ -543,6 +489,8 @@ public class Apple2Activity extends Activity { private void _disposeApple2View(Apple2MenuView apple2MenuView) { + boolean dismissedSplashScreen = false; + // Actually remove View from view hierarchy { View menuView = apple2MenuView.getView(); @@ -552,15 +500,19 @@ public class Apple2Activity extends Activity { } if (apple2MenuView == mSplashScreen) { mSplashScreen = null; + dismissedSplashScreen = true; } } // if no more views on menu stack, resume emulation if (mMenuStack.size() == 0) { dismissAllMenus(); + if (!mPausing.get()) { + nativeEmulationResume(); + } } - if (mMenuStack.size() == 0 && !mPausing.get()) { - nativeEmulationResume(); + if (!mPausing.get() && dismissedSplashScreen) { + setupGLView(); } } @@ -570,8 +522,12 @@ public class Apple2Activity extends Activity { } } - public Apple2View getView() { - return mView; + public boolean isPausing() { + return mPausing.get(); + } + + public int menuStackSize() { + return mMenuStack.size(); } public void maybeQuitApp() { diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java index 087afc80..22658418 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java @@ -16,9 +16,11 @@ package org.deadc0de.apple2ix; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.util.Log; +import android.view.ViewTreeObserver; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; @@ -50,13 +52,17 @@ class Apple2View extends GLSurfaceView { private Apple2Activity mActivity = null; + private static native void nativeOnCreate(String dataDir, int sampleRate, int monoBufferSize, int stereoBufferSize); + + private static native void nativeGraphicsInitialized(int width, int height); + + private static native void nativeGraphicsChanged(int width, int height); + + private static native void nativeRender(); + public Apple2View(Apple2Activity activity) { super(activity.getApplication()); mActivity = activity; - setup(0, 0); - } - - private void setup(int depth, int stencil) { /* By default, GLSurfaceView() creates a RGB_565 opaque surface. * If we want a translucent one, we should change the surface's @@ -75,14 +81,36 @@ class Apple2View extends GLSurfaceView { * custom config chooser. See ConfigChooser class definition * below. */ - setEGLConfigChooser(new ConfigChooser(8, 8, 8, 8, depth, stencil)); + setEGLConfigChooser(new ConfigChooser(8, 8, 8, 8, /*depth:*/0, /*stencil:*/0)); /* Set the renderer responsible for frame rendering */ setRenderer(new Renderer()); + + // Another Android Annoyance ... + // Even though we no longer use the system soft keyboard (which would definitely trigger width/height changes to our OpenGL canvas), + // we still need to listen to dimension changes, because it seems on some janky devices you have an incorrect width/height set when + // the initial OpenGL onSurfaceChanged() callback occurs. For now, include this defensive coding... + getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + public void onGlobalLayout() { + Rect rect = new Rect(); + Apple2View.this.getWindowVisibleDisplayFrame(rect); + int h = rect.height(); + int w = rect.width(); + if (w < h) { + // assure landscape dimensions + final int w_ = w; + w = h; + h = w_; + } + nativeGraphicsChanged(w, h); + } + }); + } private static class ContextFactory implements GLSurfaceView.EGLContextFactory { private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { Log.w(TAG, "creating OpenGL ES 2.0 context"); checkEglError("Before eglCreateContext", egl); @@ -295,17 +323,49 @@ class Apple2View extends GLSurfaceView { } private class Renderer implements GLSurfaceView.Renderer { + + @Override public void onDrawFrame(GL10 gl) { - Apple2View.this.mActivity.nativeRender(); + nativeRender(); } + @Override public void onSurfaceChanged(GL10 gl, int width, int height) { Apple2Preferences.GL_VENDOR.saveString(mActivity, GLES20.glGetString(GLES20.GL_VENDOR)); Apple2Preferences.GL_RENDERER.saveString(mActivity, GLES20.glGetString(GLES20.GL_RENDERER)); Apple2Preferences.GL_VERSION.saveString(mActivity, GLES20.glGetString(GLES20.GL_VERSION)); - Apple2View.this.mActivity.graphicsInitialized(width, height); + + Log.v(TAG, "graphicsInitialized(" + width + ", " + height + ")"); + + if (width < height) { + // assure landscape dimensions + final int w_ = width; + width = height; + height = w_; + } + + int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(Apple2View.this.mActivity); + int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(Apple2View.this.mActivity, /*isStereo:*/false); + int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(Apple2View.this.mActivity, /*isStereo:*/true); + Log.d(TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize); + + String dataDir = Apple2DisksMenu.getDataDir(Apple2View.this.mActivity); + nativeOnCreate(dataDir, sampleRate, monoBufferSize, stereoBufferSize); + nativeGraphicsInitialized(width, height); + + // first-time initializations #2 + if (!Apple2Preferences.FIRST_TIME_CONFIGURED.booleanValue(Apple2View.this.mActivity)) { + Apple2Preferences.KeypadPreset.IJKM_SPACE.apply(Apple2View.this.mActivity); + Apple2Preferences.FIRST_TIME_CONFIGURED.saveBoolean(Apple2View.this.mActivity, true); + } + Apple2Preferences.loadPreferences(Apple2View.this.mActivity); + + if ((Apple2View.this.mActivity.menuStackSize() == 0) && !Apple2View.this.mActivity.isPausing()) { + Apple2View.this.mActivity.nativeEmulationResume(); + } } + @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // Do nothing. } diff --git a/Android/jni/jnihooks.c b/Android/jni/jnihooks.c index 8c2ceff7..e92855eb 100644 --- a/Android/jni/jnihooks.c +++ b/Android/jni/jnihooks.c @@ -99,7 +99,7 @@ static inline int _androidTouchEvent2InterfaceEvent(jint action) { // ---------------------------------------------------------------------------- // JNI functions -void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobject obj, jstring j_dataDir, jint sampleRate, jint monoBufferSize, jint stereoBufferSize) { +void Java_org_deadc0de_apple2ix_Apple2View_nativeOnCreate(JNIEnv *env, jclass cls, jstring j_dataDir, jint sampleRate, jint monoBufferSize, jint stereoBufferSize) { const char *dataDir = (*env)->GetStringUTFChars(env, j_dataDir, 0); // Android lifecycle can call onCreate() multiple times... @@ -135,14 +135,15 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobje #endif } -void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsChanged(JNIEnv *env, jobject obj, jint width, jint height) { +void Java_org_deadc0de_apple2ix_Apple2View_nativeGraphicsChanged(JNIEnv *env, jclass cls, jint width, jint height) { + // WARNING : this can happen on non-GL thread LOG("..."); video_backend->reshape(width, height); } -void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsInitialized(JNIEnv *env, jobject obj, jint width, jint height) { +void Java_org_deadc0de_apple2ix_Apple2View_nativeGraphicsInitialized(JNIEnv *env, jclass cls, jint width, jint height) { + // WANRING : this needs to happen on the GL thread only LOG("width:%d height:%d", width, height); - video_shutdown(); video_backend->reshape(width, height); video_backend->init((void *)0); @@ -176,7 +177,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEmulationPause(JNIEnv *env, #endif } -void Java_org_deadc0de_apple2ix_Apple2Activity_nativeRender(JNIEnv *env, jobject obj) { +void Java_org_deadc0de_apple2ix_Apple2View_nativeRender(JNIEnv *env, jclass cls) { SCOPE_TRACE_VIDEO("nativeRender"); if (UNLIKELY(appState != APP_RUNNING)) {