From 9ef592a09faf46e733cc062d154aaf2483f8bcd1 Mon Sep 17 00:00:00 2001 From: Jamie Halpern Date: Thu, 21 Mar 2013 00:40:02 -0400 Subject: [PATCH] Jamie H - Updated the project version to 17 to support new APIs. Added a JNI call to allow for left joystick input from Java. Added in the key codes for the PS3 controller's X and O buttons. Joystick input for buttons is now suppressed in the NativeActivity class. --- android/app-android.cpp | 102 ++++++----- android/project.properties | 2 +- .../libnative/NativeActivity.java | 170 +++++++++++------- .../henrikrydgard/libnative/NativeApp.java | 26 +-- 4 files changed, 179 insertions(+), 121 deletions(-) diff --git a/android/app-android.cpp b/android/app-android.cpp index 6fdc010e8a..c2e28313b6 100644 --- a/android/app-android.cpp +++ b/android/app-android.cpp @@ -4,7 +4,6 @@ // It calls a set of methods defined in NativeApp.h. These should be implemented // by your game or app. - #include #include #include @@ -27,9 +26,11 @@ // For Xperia Play support enum AndroidKeyCodes { - KEYCODE_BUTTON_CROSS = 23, // trackpad or X button(Xperia Play) is pressed + KEYCODE_BUTTON_CROSS = 23, // trackpad or X button(Xperia Play) is pressed + KEYCODE_BUTTON_CROSS_PS3 = 96, // PS3 X button is pressed KEYCODE_BUTTON_CIRCLE = 1004, // Special custom keycode generated from 'O' button by our java code. Or 'O' button if Alt is pressed (TODO) - KEYCODE_BUTTON_SQUARE = 99, // Square button(Xperia Play) is pressed + KEYCODE_BUTTON_CIRCLE_PS3 = 97, // PS3 O button is pressed + KEYCODE_BUTTON_SQUARE = 99, // Square button(Xperia Play) is pressed KEYCODE_BUTTON_TRIANGLE = 100, // 'Triangle button(Xperia Play) is pressed KEYCODE_DPAD_LEFT = 21, KEYCODE_DPAD_UP = 19, @@ -50,6 +51,8 @@ const bool extraLog = true; static uint32_t pad_buttons_async_set; static uint32_t pad_buttons_async_clear; +static float left_joystick_x_async; +static float left_joystick_y_async; // Android implementation of callbacks to the Java part of the app void SystemToast(const char *text) { @@ -254,6 +257,8 @@ extern "C" void Java_com_henrikrydgard_libnative_NativeRenderer_displayRender(JN { lock_guard guard(input_state.lock); + input_state.pad_lstick_x = left_joystick_x_async; + input_state.pad_lstick_y = left_joystick_y_async; NativeUpdate(input_state); } @@ -337,50 +342,65 @@ static void AsyncUp(int padbutton) { extern "C" void Java_com_henrikrydgard_libnative_NativeApp_keyDown(JNIEnv *, jclass, jint key) { switch (key) { - case 1: AsyncDown(PAD_BUTTON_BACK); break; // Back - case 2: AsyncDown(PAD_BUTTON_MENU); break; // Menu - case 3: AsyncDown(PAD_BUTTON_A); break; // Search - case KEYCODE_BUTTON_CROSS: AsyncDown(PAD_BUTTON_A); break; - case KEYCODE_BUTTON_CIRCLE: AsyncDown(PAD_BUTTON_B); break; - case KEYCODE_BUTTON_SQUARE: AsyncDown(PAD_BUTTON_X); break; - case KEYCODE_BUTTON_TRIANGLE: AsyncDown(PAD_BUTTON_Y); break; - case KEYCODE_DPAD_LEFT: AsyncDown(PAD_BUTTON_LEFT); break; - case KEYCODE_DPAD_UP: AsyncDown(PAD_BUTTON_UP); break; - case KEYCODE_DPAD_RIGHT: AsyncDown(PAD_BUTTON_RIGHT); break; - case KEYCODE_DPAD_DOWN: AsyncDown(PAD_BUTTON_DOWN); break; - case KEYCODE_BUTTON_L1: AsyncDown(PAD_BUTTON_LBUMPER); break; - case KEYCODE_BUTTON_R1: AsyncDown(PAD_BUTTON_RBUMPER); break; - case KEYCODE_BUTTON_START: AsyncDown(PAD_BUTTON_START); break; - case KEYCODE_BUTTON_SELECT: AsyncDown(PAD_BUTTON_SELECT); break; - default: - break; + case 1: AsyncDown (PAD_BUTTON_BACK); break; // Back + case 2: AsyncDown (PAD_BUTTON_MENU); break; // Menu + case 3: AsyncDown (PAD_BUTTON_A); break; // Search + case KEYCODE_BUTTON_CROSS: + case KEYCODE_BUTTON_CROSS_PS3: + AsyncDown(PAD_BUTTON_A); + break; + case KEYCODE_BUTTON_CIRCLE: + case KEYCODE_BUTTON_CIRCLE_PS3: + AsyncDown (PAD_BUTTON_B); + break; + case KEYCODE_BUTTON_SQUARE: AsyncDown (PAD_BUTTON_X); break; + case KEYCODE_BUTTON_TRIANGLE: AsyncDown (PAD_BUTTON_Y); break; + case KEYCODE_DPAD_LEFT: AsyncDown (PAD_BUTTON_LEFT); break; + case KEYCODE_DPAD_UP: AsyncDown (PAD_BUTTON_UP); break; + case KEYCODE_DPAD_RIGHT: AsyncDown (PAD_BUTTON_RIGHT); break; + case KEYCODE_DPAD_DOWN: AsyncDown (PAD_BUTTON_DOWN); break; + case KEYCODE_BUTTON_L1: AsyncDown (PAD_BUTTON_LBUMPER); break; + case KEYCODE_BUTTON_R1: AsyncDown (PAD_BUTTON_RBUMPER); break; + case KEYCODE_BUTTON_START: AsyncDown (PAD_BUTTON_START); break; + case KEYCODE_BUTTON_SELECT: AsyncDown (PAD_BUTTON_SELECT); break; + default: break; } } extern "C" void Java_com_henrikrydgard_libnative_NativeApp_keyUp(JNIEnv *, jclass, jint key) { switch (key) { - case 1: AsyncUp(PAD_BUTTON_BACK); break; // Back - case 2: AsyncUp(PAD_BUTTON_MENU); break; // Menu - case 3: AsyncUp(PAD_BUTTON_A); break; // Search - case KEYCODE_BUTTON_CROSS: AsyncUp(PAD_BUTTON_A); break; - case KEYCODE_BUTTON_CIRCLE: AsyncUp(PAD_BUTTON_B); break; - case KEYCODE_BUTTON_SQUARE: AsyncUp(PAD_BUTTON_X); break; - case KEYCODE_BUTTON_TRIANGLE: AsyncUp(PAD_BUTTON_Y); break; - case KEYCODE_DPAD_LEFT: AsyncUp(PAD_BUTTON_LEFT); break; - case KEYCODE_DPAD_UP: AsyncUp(PAD_BUTTON_UP); break; - case KEYCODE_DPAD_RIGHT: AsyncUp(PAD_BUTTON_RIGHT); break; - case KEYCODE_DPAD_DOWN: AsyncUp(PAD_BUTTON_DOWN); break; - case KEYCODE_BUTTON_L1: AsyncUp(PAD_BUTTON_LBUMPER); break; - case KEYCODE_BUTTON_R1: AsyncUp(PAD_BUTTON_RBUMPER); break; - case KEYCODE_BUTTON_START: AsyncUp(PAD_BUTTON_START); break; - case KEYCODE_BUTTON_SELECT: AsyncUp(PAD_BUTTON_SELECT); break; - default: - break; + case 1: AsyncUp (PAD_BUTTON_BACK); break; // Back + case 2: AsyncUp (PAD_BUTTON_MENU); break; // Menu + case 3: AsyncUp (PAD_BUTTON_A); break; // Search + case KEYCODE_BUTTON_CROSS: + case KEYCODE_BUTTON_CROSS_PS3: + AsyncUp(PAD_BUTTON_A); + break; + case KEYCODE_BUTTON_CIRCLE: + case KEYCODE_BUTTON_CIRCLE_PS3: + AsyncUp (PAD_BUTTON_B); + break; + case KEYCODE_BUTTON_SQUARE: AsyncUp (PAD_BUTTON_X); break; + case KEYCODE_BUTTON_TRIANGLE: AsyncUp (PAD_BUTTON_Y); break; + case KEYCODE_DPAD_LEFT: AsyncUp (PAD_BUTTON_LEFT); break; + case KEYCODE_DPAD_UP: AsyncUp (PAD_BUTTON_UP); break; + case KEYCODE_DPAD_RIGHT: AsyncUp (PAD_BUTTON_RIGHT); break; + case KEYCODE_DPAD_DOWN: AsyncUp (PAD_BUTTON_DOWN); break; + case KEYCODE_BUTTON_L1: AsyncUp (PAD_BUTTON_LBUMPER); break; + case KEYCODE_BUTTON_R1: AsyncUp (PAD_BUTTON_RBUMPER); break; + case KEYCODE_BUTTON_START: AsyncUp (PAD_BUTTON_START); break; + case KEYCODE_BUTTON_SELECT: AsyncUp (PAD_BUTTON_SELECT); break; + default: break; } } -extern "C" void JNICALL Java_com_henrikrydgard_libnative_NativeApp_accelerometer - (JNIEnv *, jclass, float x, float y, float z) { +extern "C" void Java_com_henrikrydgard_libnative_NativeApp_joystickEvent( + JNIEnv *env, jclass, jfloat x, jfloat y) { + left_joystick_x_async = x; + left_joystick_y_async = y; +} + +extern "C" void JNICALL Java_com_henrikrydgard_libnative_NativeApp_accelerometer(JNIEnv *, jclass, float x, float y, float z) { // Theoretically this needs locking but I doubt it matters. Worst case, the X // from one "sensor frame" will be used together with Y from the next. // Should look into quantization though, for compressed movement storage. @@ -390,8 +410,7 @@ extern "C" void JNICALL Java_com_henrikrydgard_libnative_NativeApp_accelerometer input_state.acc.z = z; } -extern "C" void Java_com_henrikrydgard_libnative_NativeApp_sendMessage - (JNIEnv *env, jclass, jstring message, jstring param) { +extern "C" void Java_com_henrikrydgard_libnative_NativeApp_sendMessage(JNIEnv *env, jclass, jstring message, jstring param) { jboolean isCopy; std::string msg = GetJavaString(env, message); std::string prm = GetJavaString(env, param); @@ -399,4 +418,3 @@ extern "C" void Java_com_henrikrydgard_libnative_NativeApp_sendMessage NativeMessageReceived(msg.c_str(), prm.c_str()); } - diff --git a/android/project.properties b/android/project.properties index 4cf07c9ec1..137f19ac6a 100644 --- a/android/project.properties +++ b/android/project.properties @@ -11,5 +11,5 @@ #proguard.config=${sdk.dir}\tools\proguard\proguard-android.txt:proguard-project.txt # Project target. -target=android-10 +target=android-17 android.library=true diff --git a/android/src/com/henrikrydgard/libnative/NativeActivity.java b/android/src/com/henrikrydgard/libnative/NativeActivity.java index bf71b7b221..37561fbfa9 100644 --- a/android/src/com/henrikrydgard/libnative/NativeActivity.java +++ b/android/src/com/henrikrydgard/libnative/NativeActivity.java @@ -1,4 +1,5 @@ package com.henrikrydgard.libnative; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -7,6 +8,7 @@ import java.lang.reflect.Field; import java.util.UUID; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; @@ -29,8 +31,9 @@ import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; import android.view.Gravity; +import android.view.InputDevice; import android.view.KeyEvent; -import android.view.View; +import android.view.MotionEvent; import android.view.Window; import android.view.WindowManager; import android.widget.EditText; @@ -135,12 +138,13 @@ public class NativeActivity extends Activity { String libraryDir = getApplicationLibraryDir(appInfo); File sdcard = Environment.getExternalStorageDirectory(); Display display = ((WindowManager)this.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + @SuppressWarnings("deprecation") int scrPixelFormat = display.getPixelFormat(); @SuppressWarnings("deprecation") int scrWidth = display.getWidth(); @SuppressWarnings("deprecation") int scrHeight = display.getHeight(); - float scrRefreshRate = (float)display.getRefreshRate(); + float scrRefreshRate = display.getRefreshRate(); String externalStorageDir = sdcard.getAbsolutePath(); String dataDir = this.getFilesDir().getAbsolutePath(); String apkFilePath = appInfo.sourceDir; @@ -236,69 +240,6 @@ public class NativeActivity extends Activity { public boolean overrideKeys() { return true; } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - // Eat these keys, to avoid accidental exits / other screwups. - // Maybe there's even more we need to eat on tablets? - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - if (event.isAltPressed()) { - NativeApp.keyDown(1004); // special custom keycode - } else { - NativeApp.keyDown(1); - } - return true; - case KeyEvent.KEYCODE_MENU: - NativeApp.keyDown(2); - return true; - case KeyEvent.KEYCODE_SEARCH: - NativeApp.keyDown(3); - return true; - case KeyEvent.KEYCODE_VOLUME_DOWN: - case KeyEvent.KEYCODE_VOLUME_UP: - return false; - default: - // send the rest of the keys through. - // TODO: get rid of the three special cases above by adjusting the native side of the code. - NativeApp.keyDown(keyCode); - return true; - } - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - // Eat these keys, to avoid accidental exits / other screwups. - // Maybe there's even more we need to eat on tablets? - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - if (event.isAltPressed()) { - NativeApp.keyUp(1004); // special custom keycode - } - else if (NativeApp.isAtTopLevel()) { - return false; - } else { - NativeApp.keyUp(1); - } - return true; - case KeyEvent.KEYCODE_MENU: - // Menu should be ignored from SDK 11 forwards. We send it to the app. - NativeApp.keyUp(2); - return true; - case KeyEvent.KEYCODE_SEARCH: - // Search probably should also be ignored. We send it to the app. - NativeApp.keyUp(3); - return true; - case KeyEvent.KEYCODE_VOLUME_DOWN: - case KeyEvent.KEYCODE_VOLUME_UP: - return false; - default: - // send the rest of the keys through. - // TODO: get rid of the three special cases above by adjusting the native side of the code. - NativeApp.keyUp(keyCode); - return true; - } - } // Prevent destroying and recreating the main activity when the device rotates etc, // since this would stop the sound. @@ -309,14 +250,107 @@ public class NativeActivity extends Activity { } public static boolean inputBoxCancelled; - @SuppressWarnings("deprecation") + + @SuppressLint("NewApi") + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + // Eat these keys, to avoid accidental exits / other screwups. + // Maybe there's even more we need to eat on tablets? + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + if (event.isAltPressed()) { + NativeApp.keyDown(1004); // special custom keycode + } else { + NativeApp.keyDown(1); + } + return true; + case KeyEvent.KEYCODE_MENU: + NativeApp.keyDown(2); + return true; + case KeyEvent.KEYCODE_SEARCH: + NativeApp.keyDown(3); + return true; + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_UP: + return false; + case KeyEvent.KEYCODE_DPAD_UP: + case KeyEvent.KEYCODE_DPAD_DOWN: + case KeyEvent.KEYCODE_DPAD_LEFT: + case KeyEvent.KEYCODE_DPAD_RIGHT: + // Joysticks are supported in Honeycomb MR1 and later via the onGenericMotionEvent method. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && event.getSource() == InputDevice.SOURCE_JOYSTICK) { + return false; + } + default: + // send the rest of the keys through. + // TODO: get rid of the three special cases above by adjusting the native side of the code. + Log.d("JAMIE", "Key code: " + keyCode + ", KeyEvent: " + event); + NativeApp.keyDown(keyCode); + return true; + } + } + + @Override + @TargetApi(12) + public boolean onGenericMotionEvent(MotionEvent event) { + Log.d("JAMIE", "onGenericMotionEvent: " + event); + if (event.getSource() == InputDevice.SOURCE_JOYSTICK) { + float leftJoystickX = event.getAxisValue(MotionEvent.AXIS_X) * 1f; + float leftJoystickY = event.getAxisValue(MotionEvent.AXIS_Y) * -1f; + NativeApp.joystickEvent(leftJoystickX, leftJoystickY); + } + return false; + } + + @SuppressLint("NewApi") + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + // Eat these keys, to avoid accidental exits / other screwups. + // Maybe there's even more we need to eat on tablets? + Log.d("JAMIE", "KeyEvent: " + event); + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + if (event.isAltPressed()) { + NativeApp.keyUp(1004); // special custom keycode + } else if (NativeApp.isAtTopLevel()) { + return false; + } else { + NativeApp.keyUp(1); + } + return true; + case KeyEvent.KEYCODE_MENU: + // Menu should be ignored from SDK 11 forwards. We send it to the app. + NativeApp.keyUp(2); + return true; + case KeyEvent.KEYCODE_SEARCH: + // Search probably should also be ignored. We send it to the app. + NativeApp.keyUp(3); + return true; + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_UP: + return false; + case KeyEvent.KEYCODE_DPAD_UP: + case KeyEvent.KEYCODE_DPAD_DOWN: + case KeyEvent.KEYCODE_DPAD_LEFT: + case KeyEvent.KEYCODE_DPAD_RIGHT: + // Joysticks are supported in Honeycomb MR1 and later via the onGenericMotionEvent method. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && event.getSource() == InputDevice.SOURCE_JOYSTICK) { + return false; + } + default: + // send the rest of the keys through. + // TODO: get rid of the three special cases above by adjusting the native side of the code. + NativeApp.keyUp(keyCode); + return true; + } + } public String inputBox(String title, String defaultText, String defaultAction) { final FrameLayout fl = new FrameLayout(this); final EditText input = new EditText(this); input.setGravity(Gravity.CENTER); inputBoxCancelled = false; - FrameLayout.LayoutParams editBoxLayout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); + FrameLayout.LayoutParams editBoxLayout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); editBoxLayout.setMargins(2, 20, 2, 20); fl.addView(input, editBoxLayout); @@ -327,11 +361,13 @@ public class NativeActivity extends Activity { .setView(fl) .setTitle(title) .setPositiveButton(defaultAction, new DialogInterface.OnClickListener(){ + @Override public void onClick(DialogInterface d, int which) { d.dismiss(); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener(){ + @Override public void onClick(DialogInterface d, int which) { d.cancel(); NativeActivity.inputBoxCancelled = false; diff --git a/android/src/com/henrikrydgard/libnative/NativeApp.java b/android/src/com/henrikrydgard/libnative/NativeApp.java index 7c119ae96a..4e02203621 100644 --- a/android/src/com/henrikrydgard/libnative/NativeApp.java +++ b/android/src/com/henrikrydgard/libnative/NativeApp.java @@ -1,28 +1,32 @@ package com.henrikrydgard.libnative; -public class NativeApp { - public static native void init( - int xxres, int yyres, int dpi, String apkPath, - String dataDir, String externalDir, String libraryDir, String installID, boolean useOpenSL); +public class NativeApp { + public static native void init(int xxres, int yyres, int dpi, String apkPath, String dataDir, String externalDir, String libraryDir, String installID, boolean useOpenSL); public static native boolean isLandscape(); + public static native boolean isAtTopLevel(); - + // These have Android semantics: Resume is always called on bootup, after init public static native void pause(); + public static native void resume(); - + public static native void shutdown(); - + public static native void keyDown(int key); + public static native void keyUp(int key); + public static native void joystickEvent(float x, float y); + // will only be called between init() and shutdown() - public static native void audioRender(short [] buffer); - + public static native void audioRender(short[] buffer); + // Sensor/input data. These are asynchronous, beware! public static native void touch(float x, float y, int data, int pointerId); + public static native void accelerometer(float x, float y, float z); - + public static native void sendMessage(String msg, String arg); -} \ No newline at end of file +}