Initial commit
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
package com.example.jnicpp;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.content.Intent;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
import com.example.jnicpp.bowling.BowlingCameraActivity;
|
||||
import com.example.jnicpp.databinding.ActivityMainBinding;
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
public class MainActivity extends AppCompatActivity implements GLSurfaceView.Renderer {
|
||||
private static final String TAG = "MainActivity";
|
||||
// Used to load the 'jnicpp' library on application startup.
|
||||
static {
|
||||
try {
|
||||
System.loadLibrary("jnicpp");
|
||||
Log.d(TAG, "Native library loaded successfully");
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
Log.e(TAG, "Failed to load native library", e);
|
||||
}
|
||||
}
|
||||
private ActivityMainBinding binding;
|
||||
private GLSurfaceView glSurfaceView;
|
||||
private boolean isInitialized = false;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Log.d(TAG, "onCreate called");
|
||||
try {
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
// Setup GLSurfaceView
|
||||
glSurfaceView = binding.glSurfaceView;
|
||||
glSurfaceView.setEGLContextClientVersion(3); // OpenGL ES 3.0
|
||||
// Without this, launching an opaque Activity on top (e.g.
|
||||
// BowlingCameraActivity) destroys the GLSurfaceView's Surface, which tears
|
||||
// down the EGL context too. On return, a new context gets created and
|
||||
// initGL() runs again, but UIRenderer's one-time GL object setup
|
||||
// (UI::Init()'s s_initialized guard) doesn't know the old context - and its
|
||||
// GL handles - are gone, so it skips recreating them and the menu UI stops
|
||||
// drawing (leaving only the background demo triangle visible). Preserving
|
||||
// the context avoids that teardown/recreate cycle entirely.
|
||||
glSurfaceView.setPreserveEGLContextOnPause(true);
|
||||
glSurfaceView.setRenderer(this);
|
||||
glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
|
||||
// Touch listener attached directly to the GLSurfaceView (rather than
|
||||
// overriding Activity.onTouchEvent) so MotionEvent.getX()/getY() are
|
||||
// guaranteed relative to this view's own bounds - the same pixel space
|
||||
// our GL rendering and UI hit-testing use. Activity-level touch
|
||||
// coordinates aren't guaranteed to line up with a child view's
|
||||
// coordinates in every layout, which was causing clicks to land on the
|
||||
// wrong button.
|
||||
glSurfaceView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
int action = event.getActionMasked();
|
||||
boolean isDown = (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE);
|
||||
nativeOnTouch(event.getX(), event.getY(), isDown);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
// Example of a call to a native method
|
||||
String message = stringFromJNI();
|
||||
Log.d(TAG, "Native message: " + message);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error in onCreate", e);
|
||||
showToast("Error initializing app: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
Log.d(TAG, "onPause called");
|
||||
if (glSurfaceView != null) {
|
||||
glSurfaceView.onPause();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
Log.d(TAG, "onResume called");
|
||||
if (glSurfaceView != null) {
|
||||
glSurfaceView.onResume();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
Log.d(TAG, "onDestroy called");
|
||||
if (isInitialized) {
|
||||
try {
|
||||
cleanupGL();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error during cleanup", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Helper method to show toast from any thread
|
||||
private void showToast(final String message) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Called from native code (see PlatformBridge::LaunchBowlingCamera() in
|
||||
* my_gl_app/PlatformBridge.cpp) when the game state machine switches
|
||||
* into StateID::Menu3. Invoked via JNI from the GL render thread, so
|
||||
* this hops to the UI thread before touching Activity APIs, same as
|
||||
* showToast() above. Front/back camera switching and backing out happen
|
||||
* on BowlingCameraActivity itself (its SWITCH CAMERA and BACK buttons).
|
||||
*/
|
||||
public void launchBowlingCamera() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
startActivity(new Intent(MainActivity.this, BowlingCameraActivity.class));
|
||||
}
|
||||
});
|
||||
}
|
||||
// GLSurfaceView.Renderer implementation
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
Log.d(TAG, "Surface created");
|
||||
try {
|
||||
// Initialize OpenGL in native code (no longer needs surface parameter)
|
||||
boolean success = initGL();
|
||||
if (success) {
|
||||
isInitialized = true;
|
||||
Log.d(TAG, "OpenGL ES 3.0 initialized successfully");
|
||||
} else {
|
||||
Log.e(TAG, "Failed to initialize OpenGL ES 3.0");
|
||||
showToast("Failed to initialize OpenGL ES 3.0");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error in onSurfaceCreated", e);
|
||||
showToast("Error creating surface: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 gl, int width, int height) {
|
||||
Log.d(TAG, "Surface changed: " + width + "x" + height);
|
||||
onSurfaceResized(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawFrame(GL10 gl) {
|
||||
if (isInitialized) {
|
||||
try {
|
||||
renderFrame();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error in onDrawFrame", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A native method that is implemented by the 'jnicpp' native library,
|
||||
* which is packaged with this application.
|
||||
*/
|
||||
public native String stringFromJNI();
|
||||
/**
|
||||
* Initialize OpenGL ES 3.0 renderer
|
||||
*/
|
||||
public native boolean initGL();
|
||||
/**
|
||||
* Render a frame
|
||||
*/
|
||||
public native void renderFrame();
|
||||
/**
|
||||
* Cleanup OpenGL resources
|
||||
*/
|
||||
public native void cleanupGL();
|
||||
/**
|
||||
* Notify native code of the surface size, for UI layout/hit-testing
|
||||
*/
|
||||
public native void onSurfaceResized(int width, int height);
|
||||
/**
|
||||
* Forward a touch event to the native UI system
|
||||
*/
|
||||
public native void nativeOnTouch(float x, float y, boolean isDown);
|
||||
}
|
||||
Reference in New Issue
Block a user