Separated out SDL Android java code so audio, controller, and filesystem APIs can be used independently of the SDL activity, in Qt apps for example.

This commit is contained in:
Sam Lantinga 2017-09-22 08:30:46 -07:00
parent ad86eff1a8
commit 53b2c91d26
2 changed files with 186 additions and 680 deletions

View File

@ -56,24 +56,17 @@ public class SDLActivity extends Activity {
public static boolean mSeparateMouseAndTouch; public static boolean mSeparateMouseAndTouch;
// Main components // Main components
protected static Context mContext;
protected static SDLActivity mSingleton; protected static SDLActivity mSingleton;
protected static SDLSurface mSurface; protected static SDLSurface mSurface;
protected static View mTextEdit; protected static View mTextEdit;
protected static boolean mScreenKeyboardShown; protected static boolean mScreenKeyboardShown;
protected static ViewGroup mLayout; protected static ViewGroup mLayout;
protected static SDLJoystickHandler mJoystickHandler;
protected static SDLHapticHandler mHapticHandler;
protected static SDLClipboardHandler mClipboardHandler; protected static SDLClipboardHandler mClipboardHandler;
// This is what SDL runs in. It invokes SDL_main(), eventually // This is what SDL runs in. It invokes SDL_main(), eventually
protected static Thread mSDLThread; protected static Thread mSDLThread;
// Audio
protected static AudioTrack mAudioTrack;
protected static AudioRecord mAudioRecord;
/** /**
* This method returns the name of the shared object with the application entry point * This method returns the name of the shared object with the application entry point
* It can be overridden by derived classes. * It can be overridden by derived classes.
@ -136,17 +129,12 @@ public class SDLActivity extends Activity {
public static void initialize() { public static void initialize() {
// The static nature of the singleton and Android quirkyness force us to initialize everything here // The static nature of the singleton and Android quirkyness force us to initialize everything here
// Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
mContext = null;
mSingleton = null; mSingleton = null;
mSurface = null; mSurface = null;
mTextEdit = null; mTextEdit = null;
mLayout = null; mLayout = null;
mJoystickHandler = null;
mHapticHandler = null;
mClipboardHandler = null; mClipboardHandler = null;
mSDLThread = null; mSDLThread = null;
mAudioTrack = null;
mAudioRecord = null;
mExitCalledFromJava = false; mExitCalledFromJava = false;
mBrokenLibraries = false; mBrokenLibraries = false;
mIsResumedCalled = false; mIsResumedCalled = false;
@ -157,9 +145,6 @@ public class SDLActivity extends Activity {
} }
// Setup // Setup
public static void setContext(Context context) {
mContext = context;
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "Device: " + android.os.Build.DEVICE); Log.v(TAG, "Device: " + android.os.Build.DEVICE);
@ -167,11 +152,6 @@ public class SDLActivity extends Activity {
Log.v(TAG, "onCreate()"); Log.v(TAG, "onCreate()");
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
SDLActivity.initialize();
// So we can call stuff from static callbacks
mContext = mSingleton = this;
// Load shared libraries // Load shared libraries
String errorMsgBrokenLib = ""; String errorMsgBrokenLib = "";
try { try {
@ -209,16 +189,14 @@ public class SDLActivity extends Activity {
} }
// Set up JNI // Set up JNI
SDLActivity.nativeSetupJNI(); SDL.setupJNI();
if (Build.VERSION.SDK_INT >= 16) { // Initialize state
mJoystickHandler = new SDLJoystickHandler_API16(); SDL.initialize();
} else if (Build.VERSION.SDK_INT >= 12) {
mJoystickHandler = new SDLJoystickHandler_API12(); // So we can call stuff from static callbacks
} else { mSingleton = this;
mJoystickHandler = new SDLJoystickHandler(); SDL.setContext(this);
}
mHapticHandler = new SDLHapticHandler();
if (Build.VERSION.SDK_INT >= 11) { if (Build.VERSION.SDK_INT >= 11) {
mClipboardHandler = new SDLClipboardHandler_API11(); mClipboardHandler = new SDLClipboardHandler_API11();
@ -461,7 +439,7 @@ public class SDLActivity extends Activity {
protected static class SDLCommandHandler extends Handler { protected static class SDLCommandHandler extends Handler {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
Context context = getContext(); Context context = SDL.getContext();
if (context == null) { if (context == null) {
Log.e(TAG, "error handling message, getContext() returned null"); Log.e(TAG, "error handling message, getContext() returned null");
return; return;
@ -529,12 +507,6 @@ public class SDLActivity extends Activity {
public static native void nativeResume(); public static native void nativeResume();
public static native void onNativeDropFile(String filename); public static native void onNativeDropFile(String filename);
public static native void onNativeResize(int x, int y, int format, float rate); public static native void onNativeResize(int x, int y, int format, float rate);
public static native int onNativePadDown(int device_id, int keycode);
public static native int onNativePadUp(int device_id, int keycode);
public static native void onNativeJoy(int device_id, int axis,
float value);
public static native void onNativeHat(int device_id, int hat_id,
int x, int y);
public static native void onNativeKeyDown(int keycode); public static native void onNativeKeyDown(int keycode);
public static native void onNativeKeyUp(int keycode); public static native void onNativeKeyUp(int keycode);
public static native void onNativeKeyboardFocusLost(); public static native void onNativeKeyboardFocusLost();
@ -546,12 +518,6 @@ public class SDLActivity extends Activity {
public static native void onNativeClipboardChanged(); public static native void onNativeClipboardChanged();
public static native void onNativeSurfaceChanged(); public static native void onNativeSurfaceChanged();
public static native void onNativeSurfaceDestroyed(); public static native void onNativeSurfaceDestroyed();
public static native int nativeAddJoystick(int device_id, String name, String desc,
int is_accelerometer, int nbuttons,
int naxes, int nhats, int nballs);
public static native int nativeRemoveJoystick(int device_id);
public static native int nativeAddHaptic(int device_id, String name);
public static native int nativeRemoveHaptic(int device_id);
public static native String nativeGetHint(String name); public static native String nativeGetHint(String name);
/** /**
@ -632,7 +598,7 @@ public class SDLActivity extends Activity {
return false; return false;
} }
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isAcceptingText()) { if (imm.isAcceptingText()) {
return true; return true;
} }
@ -654,7 +620,7 @@ public class SDLActivity extends Activity {
* This method is called by SDL using JNI. * This method is called by SDL using JNI.
*/ */
public static Context getContext() { public static Context getContext() {
return mContext; return SDL.getContext();
} }
static class ShowTextInputTask implements Runnable { static class ShowTextInputTask implements Runnable {
@ -681,7 +647,7 @@ public class SDLActivity extends Activity {
params.topMargin = y; params.topMargin = y;
if (mTextEdit == null) { if (mTextEdit == null) {
mTextEdit = new DummyEdit(getContext()); mTextEdit = new DummyEdit(SDL.getContext());
mLayout.addView(mTextEdit, params); mLayout.addView(mTextEdit, params);
} else { } else {
@ -691,7 +657,7 @@ public class SDLActivity extends Activity {
mTextEdit.setVisibility(View.VISIBLE); mTextEdit.setVisibility(View.VISIBLE);
mTextEdit.requestFocus(); mTextEdit.requestFocus();
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(mTextEdit, 0); imm.showSoftInput(mTextEdit, 0);
mScreenKeyboardShown = true; mScreenKeyboardShown = true;
@ -731,156 +697,6 @@ public class SDLActivity extends Activity {
return SDLActivity.mSurface.getNativeSurface(); return SDLActivity.mSurface.getNativeSurface();
} }
// Audio
/**
* This method is called by SDL using JNI.
*/
public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
Log.v(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
if (mAudioTrack == null) {
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
Log.e(TAG, "Failed during initialization of Audio Track");
mAudioTrack = null;
return -1;
}
mAudioTrack.play();
}
Log.v(TAG, "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
return 0;
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteShortBuffer(short[] buffer) {
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(short)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteByteBuffer(byte[] buffer) {
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(byte)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
Log.v(TAG, "SDL capture: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
if (mAudioRecord == null) {
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize);
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Failed during initialization of AudioRecord");
mAudioRecord.release();
mAudioRecord = null;
return -1;
}
mAudioRecord.startRecording();
}
Log.v(TAG, "SDL capture: got " + ((mAudioRecord.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioRecord.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
return 0;
}
/** This method is called by SDL using JNI. */
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
// !!! FIXME: this is available in API Level 23. Until then, we always block. :(
//return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
return mAudioRecord.read(buffer, 0, buffer.length);
}
/** This method is called by SDL using JNI. */
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
// !!! FIXME: this is available in API Level 23. Until then, we always block. :(
//return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
return mAudioRecord.read(buffer, 0, buffer.length);
}
/** This method is called by SDL using JNI. */
public static void audioClose() {
if (mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
}
/** This method is called by SDL using JNI. */
public static void captureClose() {
if (mAudioRecord != null) {
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
}
}
// Input // Input
/** /**
@ -900,67 +716,20 @@ public class SDLActivity extends Activity {
return Arrays.copyOf(filtered, used); return Arrays.copyOf(filtered, used);
} }
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
public static boolean handleJoystickMotionEvent(MotionEvent event) {
return mJoystickHandler.handleMotionEvent(event);
}
/**
* This method is called by SDL using JNI.
*/
public static void pollInputDevices() {
if (SDLActivity.mSDLThread != null) {
mJoystickHandler.pollInputDevices();
}
}
/**
* This method is called by SDL using JNI.
*/
public static void pollHapticDevices() {
if (SDLActivity.mSDLThread != null) {
mHapticHandler.pollHapticDevices();
}
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticRun(int device_id, int length) {
if (SDLActivity.mSDLThread != null) {
mHapticHandler.run(device_id, length);
}
}
// Check if a given device is considered a possible SDL joystick
public static boolean isDeviceSDLJoystick(int deviceId) {
InputDevice device = InputDevice.getDevice(deviceId);
// We cannot use InputDevice.isVirtual before API 16, so let's accept
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
if ((device == null) || (deviceId < 0)) {
return false;
}
int sources = device.getSources();
return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
);
}
// APK expansion files support // APK expansion files support
/** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */ /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
private Object expansionFile; private static Object expansionFile;
/** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */ /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */
private Method expansionFileMethod; private static Method expansionFileMethod;
/** /**
* This method is called by SDL using JNI. * This method is called by SDL using JNI.
* @return an InputStream on success or null if no expansion file was used. * @return an InputStream on success or null if no expansion file was used.
* @throws IOException on errors. Message is set for the SDL error message. * @throws IOException on errors. Message is set for the SDL error message.
*/ */
public InputStream openAPKExpansionInputStream(String fileName) throws IOException { public static InputStream openAPKExpansionInputStream(String fileName) throws IOException {
// Get a ZipResourceFile representing a merger of both the main and patch files // Get a ZipResourceFile representing a merger of both the main and patch files
if (expansionFile == null) { if (expansionFile == null) {
String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION"); String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION");
@ -987,7 +756,7 @@ public class SDLActivity extends Activity {
// not a part of Android SDK we access it using reflection // not a part of Android SDK we access it using reflection
expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport") expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport")
.getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class) .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
.invoke(null, this, mainVersion, patchVersion); .invoke(null, SDL.getContext(), mainVersion, patchVersion);
expansionFileMethod = expansionFile.getClass() expansionFileMethod = expansionFile.getClass()
.getMethod("getInputStream", String.class); .getMethod("getInputStream", String.class);
@ -1452,14 +1221,14 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
// Furthermore, it's possible a game controller has SOURCE_KEYBOARD and // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
// SOURCE_JOYSTICK, while its key events arrive from the keyboard source // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
// So, retrieve the device itself and check all of its sources // So, retrieve the device itself and check all of its sources
if (SDLActivity.isDeviceSDLJoystick(event.getDeviceId())) { if (SDLControllerManager.isDeviceSDLJoystick(event.getDeviceId())) {
// Note that we process events with specific key codes here // Note that we process events with specific key codes here
if (event.getAction() == KeyEvent.ACTION_DOWN) { if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) { if (SDLControllerManager.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
return true; return true;
} }
} else if (event.getAction() == KeyEvent.ACTION_UP) { } else if (event.getAction() == KeyEvent.ACTION_UP) {
if (SDLActivity.onNativePadUp(event.getDeviceId(), keyCode) == 0) { if (SDLControllerManager.onNativePadUp(event.getDeviceId(), keyCode) == 0) {
return true; return true;
} }
} }
@ -1759,331 +1528,6 @@ class SDLInputConnection extends BaseInputConnection {
} }
} }
/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
class SDLJoystickHandler {
/**
* Handles given MotionEvent.
* @param event the event to be handled.
* @return if given event was processed.
*/
public boolean handleMotionEvent(MotionEvent event) {
return false;
}
/**
* Handles adding and removing of input devices.
*/
public void pollInputDevices() {
}
}
/* Actual joystick functionality available for API >= 12 devices */
class SDLJoystickHandler_API12 extends SDLJoystickHandler {
static class SDLJoystick {
public int device_id;
public String name;
public String desc;
public ArrayList<InputDevice.MotionRange> axes;
public ArrayList<InputDevice.MotionRange> hats;
}
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
@Override
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
return arg0.getAxis() - arg1.getAxis();
}
}
private ArrayList<SDLJoystick> mJoysticks;
public SDLJoystickHandler_API12() {
mJoysticks = new ArrayList<SDLJoystick>();
}
@Override
public void pollInputDevices() {
int[] deviceIds = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller
for(int i=deviceIds.length-1; i>-1; i--) {
SDLJoystick joystick = getJoystick(deviceIds[i]);
if (joystick == null) {
joystick = new SDLJoystick();
InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
if (SDLActivity.isDeviceSDLJoystick(deviceIds[i])) {
joystick.device_id = deviceIds[i];
joystick.name = joystickDevice.getName();
joystick.desc = getJoystickDescriptor(joystickDevice);
joystick.axes = new ArrayList<InputDevice.MotionRange>();
joystick.hats = new ArrayList<InputDevice.MotionRange>();
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator());
for (InputDevice.MotionRange range : ranges ) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
range.getAxis() == MotionEvent.AXIS_HAT_Y) {
joystick.hats.add(range);
}
else {
joystick.axes.add(range);
}
}
}
mJoysticks.add(joystick);
SDLActivity.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1,
joystick.axes.size(), joystick.hats.size()/2, 0);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = new ArrayList<Integer>();
for(int i=0; i < mJoysticks.size(); i++) {
int device_id = mJoysticks.get(i).device_id;
int j;
for (j=0; j < deviceIds.length; j++) {
if (device_id == deviceIds[j]) break;
}
if (j == deviceIds.length) {
removedDevices.add(Integer.valueOf(device_id));
}
}
for(int i=0; i < removedDevices.size(); i++) {
int device_id = removedDevices.get(i).intValue();
SDLActivity.nativeRemoveJoystick(device_id);
for (int j=0; j < mJoysticks.size(); j++) {
if (mJoysticks.get(j).device_id == device_id) {
mJoysticks.remove(j);
break;
}
}
}
}
protected SDLJoystick getJoystick(int device_id) {
for(int i=0; i < mJoysticks.size(); i++) {
if (mJoysticks.get(i).device_id == device_id) {
return mJoysticks.get(i);
}
}
return null;
}
@Override
public boolean handleMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
int actionPointerIndex = event.getActionIndex();
int action = event.getActionMasked();
switch(action) {
case MotionEvent.ACTION_MOVE:
SDLJoystick joystick = getJoystick(event.getDeviceId());
if ( joystick != null ) {
for (int i = 0; i < joystick.axes.size(); i++) {
InputDevice.MotionRange range = joystick.axes.get(i);
/* Normalize the value to -1...1 */
float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
SDLActivity.onNativeJoy(joystick.device_id, i, value );
}
for (int i = 0; i < joystick.hats.size(); i+=2) {
int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
SDLActivity.onNativeHat(joystick.device_id, i/2, hatX, hatY );
}
}
break;
default:
break;
}
}
return true;
}
public String getJoystickDescriptor(InputDevice joystickDevice) {
return joystickDevice.getName();
}
}
class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
@Override
public String getJoystickDescriptor(InputDevice joystickDevice) {
String desc = joystickDevice.getDescriptor();
if (desc != null && desc != "") {
return desc;
}
return super.getJoystickDescriptor(joystickDevice);
}
}
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
// Generic Motion (mouse hover, joystick...) events go here
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
float x, y;
int action;
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
case InputDevice.SOURCE_GAMEPAD:
case InputDevice.SOURCE_DPAD:
return SDLActivity.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
if (!SDLActivity.mSeparateMouseAndTouch) {
break;
}
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y);
return true;
default:
break;
}
break;
default:
break;
}
// Event was not managed
return false;
}
}
class SDLHapticHandler {
class SDLHaptic {
public int device_id;
public String name;
public Vibrator vib;
}
private ArrayList<SDLHaptic> mHaptics;
public SDLHapticHandler() {
mHaptics = new ArrayList<SDLHaptic>();
}
public void run(int device_id, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.vibrate (length);
}
}
public void pollHapticDevices() {
final int deviceId_VIBRATOR_SERVICE = 999999;
boolean hasVibratorService = false;
int[] deviceIds = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller
if (Build.VERSION.SDK_INT >= 16)
{
for (int i = deviceIds.length-1; i > -1; i--) {
SDLHaptic haptic = getHaptic(deviceIds[i]);
if (haptic == null) {
InputDevice device = InputDevice.getDevice(deviceIds[i]);
Vibrator vib = device.getVibrator();
if (vib.hasVibrator()) {
haptic = new SDLHaptic();
haptic.device_id = deviceIds[i];
haptic.name = device.getName();
haptic.vib = vib;
mHaptics.add(haptic);
SDLActivity.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
}
/* Check VIBRATOR_SERVICE */
Vibrator vib = (Vibrator) SDLActivity.mSingleton.getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (vib != null) {
if (Build.VERSION.SDK_INT >= 11) {
hasVibratorService = vib.hasVibrator();
} else {
hasVibratorService = true;
}
if (hasVibratorService) {
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
if (haptic == null) {
haptic = new SDLHaptic();
haptic.device_id = deviceId_VIBRATOR_SERVICE;
haptic.name = "VIBRATOR_SERVICE";
haptic.vib = vib;
mHaptics.add(haptic);
SDLActivity.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = new ArrayList<Integer>();
for(int i=0; i < mHaptics.size(); i++) {
int device_id = mHaptics.get(i).device_id;
int j;
for (j=0; j < deviceIds.length; j++) {
if (device_id == deviceIds[j]) break;
}
if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) {
// don't remove the vibrator if it is still present
} else if (j == deviceIds.length) {
removedDevices.add(device_id);
}
}
for(int i=0; i < removedDevices.size(); i++) {
int device_id = removedDevices.get(i);
SDLActivity.nativeRemoveHaptic(device_id);
for (int j=0; j < mHaptics.size(); j++) {
if (mHaptics.get(j).device_id == device_id) {
mHaptics.remove(j);
break;
}
}
}
}
protected SDLHaptic getHaptic(int device_id) {
for(int i=0; i < mHaptics.size(); i++) {
if (mHaptics.get(i).device_id == device_id) {
return mHaptics.get(i);
}
}
return null;
}
}
interface SDLClipboardHandler { interface SDLClipboardHandler {
public boolean clipboardHasText(); public boolean clipboardHasText();
@ -2100,7 +1544,7 @@ class SDLClipboardHandler_API11 implements
protected android.content.ClipboardManager mClipMgr; protected android.content.ClipboardManager mClipMgr;
SDLClipboardHandler_API11() { SDLClipboardHandler_API11() {
mClipMgr = (android.content.ClipboardManager) SDLActivity.mSingleton.getContext().getSystemService(Context.CLIPBOARD_SERVICE); mClipMgr = (android.content.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
mClipMgr.addPrimaryClipChangedListener(this); mClipMgr.addPrimaryClipChangedListener(this);
} }
@ -2139,7 +1583,7 @@ class SDLClipboardHandler_Old implements
protected android.text.ClipboardManager mClipMgrOld; protected android.text.ClipboardManager mClipMgrOld;
SDLClipboardHandler_Old() { SDLClipboardHandler_Old() {
mClipMgrOld = (android.text.ClipboardManager) SDLActivity.mSingleton.getContext().getSystemService(Context.CLIPBOARD_SERVICE); mClipMgrOld = (android.text.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
} }
@Override @Override
@ -2164,4 +1608,3 @@ class SDLClipboardHandler_Old implements
} }
} }

View File

@ -56,6 +56,8 @@
#define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function) #define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function)
#define CONCAT2(prefix, class, function) Java_ ## prefix ## _ ## class ## _ ## function #define CONCAT2(prefix, class, function) Java_ ## prefix ## _ ## class ## _ ## function
#define SDL_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function) #define SDL_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function)
#define SDL_JAVA_AUDIO_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLAudioManager, function)
#define SDL_JAVA_CONTROLLER_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
#define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function) #define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
@ -75,39 +77,6 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
JNIEnv* env, jclass jcls, JNIEnv* env, jclass jcls,
jint width, jint height, jint format, jfloat rate); jint width, jint height, jint format, jfloat rate);
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadDown)(
JNIEnv* env, jclass jcls,
jint device_id, jint keycode);
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadUp)(
JNIEnv* env, jclass jcls,
jint device_id, jint keycode);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeJoy)(
JNIEnv* env, jclass jcls,
jint device_id, jint axis, jfloat value);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeHat)(
JNIEnv* env, jclass jcls,
jint device_id, jint hat_id, jint x, jint y);
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddJoystick)(
JNIEnv* env, jclass jcls,
jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
jint nbuttons, jint naxes, jint nhats, jint nballs);
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveJoystick)(
JNIEnv* env, jclass jcls,
jint device_id);
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddHaptic)(
JNIEnv* env, jclass jcls,
jint device_id, jstring device_name);
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveHaptic)(
JNIEnv* env, jclass jcls,
jint device_id);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)( JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(
JNIEnv* env, jclass jcls); JNIEnv* env, jclass jcls);
@ -166,6 +135,48 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingTex
JNIEnv* env, jclass cls, JNIEnv* env, jclass cls,
jstring text, jint newCursorPosition); jstring text, jint newCursorPosition);
/* Java class SDLAudioManager */
JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
JNIEnv *env, jclass jcls);
/* Java class SDLControllerManager */
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(
JNIEnv *env, jclass jcls);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
JNIEnv* env, jclass jcls,
jint device_id, jint keycode);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
JNIEnv* env, jclass jcls,
jint device_id, jint keycode);
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
JNIEnv* env, jclass jcls,
jint device_id, jint axis, jfloat value);
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
JNIEnv* env, jclass jcls,
jint device_id, jint hat_id, jint x, jint y);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
JNIEnv* env, jclass jcls,
jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
jint nbuttons, jint naxes, jint nhats, jint nballs);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
JNIEnv* env, jclass jcls,
jint device_id);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
JNIEnv* env, jclass jcls,
jint device_id, jstring device_name);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
JNIEnv* env, jclass jcls,
jint device_id);
/* Uncomment this to log messages entering and exiting methods in this file */ /* Uncomment this to log messages entering and exiting methods in this file */
/* #define DEBUG_JNI */ /* #define DEBUG_JNI */
@ -189,17 +200,6 @@ static jclass mActivityClass;
/* method signatures */ /* method signatures */
static jmethodID midGetNativeSurface; static jmethodID midGetNativeSurface;
static jmethodID midAudioOpen;
static jmethodID midAudioWriteShortBuffer;
static jmethodID midAudioWriteByteBuffer;
static jmethodID midAudioClose;
static jmethodID midCaptureOpen;
static jmethodID midCaptureReadShortBuffer;
static jmethodID midCaptureReadByteBuffer;
static jmethodID midCaptureClose;
static jmethodID midPollInputDevices;
static jmethodID midPollHapticDevices;
static jmethodID midHapticRun;
static jmethodID midSetActivityTitle; static jmethodID midSetActivityTitle;
static jmethodID midSetOrientation; static jmethodID midSetOrientation;
static jmethodID midGetContext; static jmethodID midGetContext;
@ -210,7 +210,28 @@ static jmethodID midIsScreenKeyboardShown;
static jmethodID midClipboardSetText; static jmethodID midClipboardSetText;
static jmethodID midClipboardGetText; static jmethodID midClipboardGetText;
static jmethodID midClipboardHasText; static jmethodID midClipboardHasText;
static jmethodID midOpenAPKExpansionInputStream;
/* audio manager */
static jclass mAudioManagerClass;
/* method signatures */
static jmethodID midAudioOpen;
static jmethodID midAudioWriteShortBuffer;
static jmethodID midAudioWriteByteBuffer;
static jmethodID midAudioClose;
static jmethodID midCaptureOpen;
static jmethodID midCaptureReadShortBuffer;
static jmethodID midCaptureReadByteBuffer;
static jmethodID midCaptureClose;
/* controller manager */
static jclass mControllerManagerClass;
/* method signatures */
static jmethodID midPollInputDevices;
static jmethodID midPollHapticDevices;
static jmethodID midHapticRun;
/* static fields */ /* static fields */
static jfieldID fidSeparateMouseAndTouch; static jfieldID fidSeparateMouseAndTouch;
@ -245,7 +266,17 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
return JNI_VERSION_1_4; return JNI_VERSION_1_4;
} }
/* Called before SDL_main() to initialize JNI bindings */ void checkJNIReady()
{
if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) {
// We aren't fully initialized, let's just return.
return;
}
SDL_SetMainReady();
}
/* Activity initialization -- called before SDL_main() to initialize JNI bindings */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls) JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
{ {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()"); __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()");
@ -256,28 +287,6 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass c
midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"getNativeSurface","()Landroid/view/Surface;"); "getNativeSurface","()Landroid/view/Surface;");
midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"audioOpen", "(IZZI)I");
midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"audioWriteShortBuffer", "([S)V");
midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"audioWriteByteBuffer", "([B)V");
midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"audioClose", "()V");
midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"captureOpen", "(IZZI)I");
midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"captureReadShortBuffer", "([SZ)I");
midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"captureReadByteBuffer", "([BZ)I");
midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"captureClose", "()V");
midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"pollInputDevices", "()V");
midPollHapticDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"pollHapticDevices", "()V");
midHapticRun = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"hapticRun", "(II)V");
midSetActivityTitle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, midSetActivityTitle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"setActivityTitle","(Ljava/lang/String;)Z"); "setActivityTitle","(Ljava/lang/String;)Z");
midSetOrientation = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, midSetOrientation = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
@ -298,16 +307,14 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass c
"clipboardGetText", "()Ljava/lang/String;"); "clipboardGetText", "()Ljava/lang/String;");
midClipboardHasText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, midClipboardHasText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"clipboardHasText", "()Z"); "clipboardHasText", "()Z");
midOpenAPKExpansionInputStream = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
bHasNewData = SDL_FALSE; "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
if (!midGetNativeSurface || if (!midGetNativeSurface ||
!midAudioOpen || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioClose ||
!midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose ||
!midPollInputDevices || !midPollHapticDevices || !midHapticRun ||
!midSetActivityTitle || !midSetOrientation || !midGetContext || !midInputGetInputDeviceIds || !midSetActivityTitle || !midSetOrientation || !midGetContext || !midInputGetInputDeviceIds ||
!midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown || !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||
!midClipboardSetText || !midClipboardGetText || !midClipboardHasText) { !midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
!midOpenAPKExpansionInputStream) {
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?"); __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
} }
@ -317,7 +324,64 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass c
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java static fields, do you have the latest version of SDLActivity.java?"); __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java static fields, do you have the latest version of SDLActivity.java?");
} }
SDL_SetMainReady(); checkJNIReady();
}
/* Audio initialization -- called before SDL_main() to initialize JNI bindings */
JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
{
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()");
Android_JNI_SetupThread();
mAudioManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
"audioOpen", "(IZZI)I");
midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
"audioWriteShortBuffer", "([S)V");
midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
"audioWriteByteBuffer", "([B)V");
midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
"audioClose", "()V");
midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
"captureOpen", "(IZZI)I");
midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
"captureReadShortBuffer", "([SZ)I");
midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
"captureReadByteBuffer", "([BZ)I");
midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
"captureClose", "()V");
if (!midAudioOpen || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioClose ||
!midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose) {
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
}
checkJNIReady();
}
/* Controller initialization -- called before SDL_main() to initialize JNI bindings */
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
{
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()");
Android_JNI_SetupThread();
mControllerManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
"pollInputDevices", "()V");
midPollHapticDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
"pollHapticDevices", "()V");
midHapticRun = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
"hapticRun", "(II)V");
if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun) {
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
}
checkJNIReady();
} }
/* SDL main function prototype */ /* SDL main function prototype */
@ -421,7 +485,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
} }
/* Paddown */ /* Paddown */
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadDown)( JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
JNIEnv* env, jclass jcls, JNIEnv* env, jclass jcls,
jint device_id, jint keycode) jint device_id, jint keycode)
{ {
@ -429,7 +493,7 @@ JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadDown)(
} }
/* Padup */ /* Padup */
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadUp)( JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
JNIEnv* env, jclass jcls, JNIEnv* env, jclass jcls,
jint device_id, jint keycode) jint device_id, jint keycode)
{ {
@ -437,7 +501,7 @@ JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadUp)(
} }
/* Joy */ /* Joy */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeJoy)( JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
JNIEnv* env, jclass jcls, JNIEnv* env, jclass jcls,
jint device_id, jint axis, jfloat value) jint device_id, jint axis, jfloat value)
{ {
@ -445,7 +509,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeJoy)(
} }
/* POV Hat */ /* POV Hat */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeHat)( JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
JNIEnv* env, jclass jcls, JNIEnv* env, jclass jcls,
jint device_id, jint hat_id, jint x, jint y) jint device_id, jint hat_id, jint x, jint y)
{ {
@ -453,7 +517,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeHat)(
} }
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddJoystick)( JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
JNIEnv* env, jclass jcls, JNIEnv* env, jclass jcls,
jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer, jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
jint nbuttons, jint naxes, jint nhats, jint nballs) jint nbuttons, jint naxes, jint nhats, jint nballs)
@ -470,14 +534,14 @@ JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddJoystick)(
return retval; return retval;
} }
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveJoystick)( JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
JNIEnv* env, jclass jcls, JNIEnv* env, jclass jcls,
jint device_id) jint device_id)
{ {
return Android_RemoveJoystick(device_id); return Android_RemoveJoystick(device_id);
} }
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddHaptic)( JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
JNIEnv* env, jclass jcls, jint device_id, jstring device_name) JNIEnv* env, jclass jcls, jint device_id, jstring device_name)
{ {
int retval; int retval;
@ -490,7 +554,7 @@ JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddHaptic)(
return retval; return retval;
} }
JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveHaptic)( JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
JNIEnv* env, jclass jcls, jint device_id) JNIEnv* env, jclass jcls, jint device_id)
{ {
return Android_RemoveHaptic(device_id); return Android_RemoveHaptic(device_id);
@ -882,7 +946,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int
if (iscapture) { if (iscapture) {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture"); __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
captureBuffer16Bit = is16Bit; captureBuffer16Bit = is16Bit;
if ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) { if ((*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
/* Error during audio initialization */ /* Error during audio initialization */
__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioRecord initialization!"); __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioRecord initialization!");
return 0; return 0;
@ -890,7 +954,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int
} else { } else {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output"); __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
audioBuffer16Bit = is16Bit; audioBuffer16Bit = is16Bit;
if ((*env)->CallStaticIntMethod(env, mActivityClass, midAudioOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) { if ((*env)->CallStaticIntMethod(env, mAudioManagerClass, midAudioOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
/* Error during audio initialization */ /* Error during audio initialization */
__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!"); __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
return 0; return 0;
@ -958,10 +1022,10 @@ void Android_JNI_WriteAudioBuffer(void)
if (audioBuffer16Bit) { if (audioBuffer16Bit) {
(*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT); (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
(*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer); (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
} else { } else {
(*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT); (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
(*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer); (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
} }
/* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */ /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
@ -975,7 +1039,7 @@ int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
if (captureBuffer16Bit) { if (captureBuffer16Bit) {
SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / 2)); SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / 2));
br = (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE); br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
if (br > 0) { if (br > 0) {
jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy); jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
br *= 2; br *= 2;
@ -984,7 +1048,7 @@ int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
} }
} else { } else {
SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen); SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen);
br = (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE); br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
if (br > 0) { if (br > 0) {
jbyte *ptr = (*env)->GetByteArrayElements(env, (jbyteArray)captureBuffer, &isCopy); jbyte *ptr = (*env)->GetByteArrayElements(env, (jbyteArray)captureBuffer, &isCopy);
SDL_memcpy(buffer, ptr, br); SDL_memcpy(buffer, ptr, br);
@ -1008,9 +1072,9 @@ void Android_JNI_FlushCapturedAudio(void)
} }
#else #else
if (captureBuffer16Bit) { if (captureBuffer16Bit) {
(*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE); (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
} else { } else {
(*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE); (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
} }
#endif #endif
} }
@ -1020,13 +1084,13 @@ void Android_JNI_CloseAudioDevice(const int iscapture)
JNIEnv *env = Android_JNI_GetEnv(); JNIEnv *env = Android_JNI_GetEnv();
if (iscapture) { if (iscapture) {
(*env)->CallStaticVoidMethod(env, mActivityClass, midCaptureClose); (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midCaptureClose);
if (captureBuffer) { if (captureBuffer) {
(*env)->DeleteGlobalRef(env, captureBuffer); (*env)->DeleteGlobalRef(env, captureBuffer);
captureBuffer = NULL; captureBuffer = NULL;
} }
} else { } else {
(*env)->CallStaticVoidMethod(env, mActivityClass, midAudioClose); (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioClose);
if (audioBuffer) { if (audioBuffer) {
(*env)->DeleteGlobalRef(env, audioBuffer); (*env)->DeleteGlobalRef(env, audioBuffer);
audioBuffer = NULL; audioBuffer = NULL;
@ -1160,13 +1224,7 @@ fallback:
inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */); inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */);
if (Android_JNI_ExceptionOccurred(SDL_FALSE)) { if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
/* Try fallback to APK expansion files */ /* Try fallback to APK expansion files */
mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context), inputStream = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, midOpenAPKExpansionInputStream, fileNameJString);
"openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
if (!mid) {
SDL_SetError("No openAPKExpansionInputStream() in Java class");
goto failure; /* Java class is missing the required method */
}
inputStream = (*mEnv)->CallObjectMethod(mEnv, context, mid, fileNameJString);
/* Exception is checked first because it always needs to be cleared. /* Exception is checked first because it always needs to be cleared.
* If no exception occurred then the last SDL error message is kept. * If no exception occurred then the last SDL error message is kept.
@ -1673,19 +1731,19 @@ void Android_JNI_SetSeparateMouseAndTouch(SDL_bool new_value)
void Android_JNI_PollInputDevices(void) void Android_JNI_PollInputDevices(void)
{ {
JNIEnv *env = Android_JNI_GetEnv(); JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mActivityClass, midPollInputDevices); (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices);
} }
void Android_JNI_PollHapticDevices(void) void Android_JNI_PollHapticDevices(void)
{ {
JNIEnv *env = Android_JNI_GetEnv(); JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mActivityClass, midPollHapticDevices); (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollHapticDevices);
} }
void Android_JNI_HapticRun(int device_id, int length) void Android_JNI_HapticRun(int device_id, int length)
{ {
JNIEnv *env = Android_JNI_GetEnv(); JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mActivityClass, midHapticRun, device_id, length); (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRun, device_id, length);
} }
@ -1862,6 +1920,11 @@ const char * SDL_AndroidGetInternalStoragePath(void)
/* context = SDLActivity.getContext(); */ /* context = SDLActivity.getContext(); */
context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
if (!context) {
SDL_SetError("Couldn't get Android context!");
LocalReferenceHolder_Cleanup(&refs);
return NULL;
}
/* fileObj = context.getFilesDir(); */ /* fileObj = context.getFilesDir(); */
mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),