Browse Source

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.

Sam Lantinga 7 years ago
parent
commit
53b2c91d26
2 changed files with 186 additions and 680 deletions
  1. 21 578
      android-project/src/org/libsdl/app/SDLActivity.java
  2. 165 102
      src/core/android/SDL_android.c

+ 21 - 578
android-project/src/org/libsdl/app/SDLActivity.java

@@ -56,24 +56,17 @@ public class SDLActivity extends Activity {
     public static boolean mSeparateMouseAndTouch;
 
     // Main components
-    protected static Context mContext;
     protected static SDLActivity mSingleton;
     protected static SDLSurface mSurface;
     protected static View mTextEdit;
     protected static boolean mScreenKeyboardShown;
     protected static ViewGroup mLayout;
-    protected static SDLJoystickHandler mJoystickHandler;
-    protected static SDLHapticHandler mHapticHandler;
     protected static SDLClipboardHandler mClipboardHandler;
 
 
     // This is what SDL runs in. It invokes SDL_main(), eventually
     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
      * It can be overridden by derived classes.
@@ -136,17 +129,12 @@ public class SDLActivity extends Activity {
     public static void initialize() {
         // 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
-        mContext = null;
         mSingleton = null;
         mSurface = null;
         mTextEdit = null;
         mLayout = null;
-        mJoystickHandler = null;
-        mHapticHandler = null;
         mClipboardHandler = null;
         mSDLThread = null;
-        mAudioTrack = null;
-        mAudioRecord = null;
         mExitCalledFromJava = false;
         mBrokenLibraries = false;
         mIsResumedCalled = false;
@@ -157,9 +145,6 @@ public class SDLActivity extends Activity {
     }
 
     // Setup
-    public static void setContext(Context context) {
-        mContext = context;
-    }
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         Log.v(TAG, "Device: " + android.os.Build.DEVICE);
@@ -167,11 +152,6 @@ public class SDLActivity extends Activity {
         Log.v(TAG, "onCreate()");
         super.onCreate(savedInstanceState);
 
-        SDLActivity.initialize();
-
-        // So we can call stuff from static callbacks
-        mContext = mSingleton = this;
-
         // Load shared libraries
         String errorMsgBrokenLib = "";
         try {
@@ -209,16 +189,14 @@ public class SDLActivity extends Activity {
         }
 
         // Set up JNI
-        SDLActivity.nativeSetupJNI();
+        SDL.setupJNI();
 
-        if (Build.VERSION.SDK_INT >= 16) {
-            mJoystickHandler = new SDLJoystickHandler_API16();
-        } else if (Build.VERSION.SDK_INT >= 12) {
-            mJoystickHandler = new SDLJoystickHandler_API12();
-        } else {
-            mJoystickHandler = new SDLJoystickHandler();
-        }
-        mHapticHandler = new SDLHapticHandler();
+        // Initialize state
+        SDL.initialize();
+
+        // So we can call stuff from static callbacks
+        mSingleton = this;
+        SDL.setContext(this);
 
         if (Build.VERSION.SDK_INT >= 11) {
             mClipboardHandler = new SDLClipboardHandler_API11();
@@ -461,7 +439,7 @@ public class SDLActivity extends Activity {
     protected static class SDLCommandHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
-            Context context = getContext();
+            Context context = SDL.getContext();
             if (context == null) {
                 Log.e(TAG, "error handling message, getContext() returned null");
                 return;
@@ -529,12 +507,6 @@ public class SDLActivity extends Activity {
     public static native void nativeResume();
     public static native void onNativeDropFile(String filename);
     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 onNativeKeyUp(int keycode);
     public static native void onNativeKeyboardFocusLost();
@@ -546,12 +518,6 @@ public class SDLActivity extends Activity {
     public static native void onNativeClipboardChanged();
     public static native void onNativeSurfaceChanged();
     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);
 
     /**
@@ -632,7 +598,7 @@ public class SDLActivity extends Activity {
           return false;
        }
 
-       InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+       InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm.isAcceptingText()) {
           return true;
        }
@@ -654,7 +620,7 @@ public class SDLActivity extends Activity {
      * This method is called by SDL using JNI.
      */
     public static Context getContext() {
-        return mContext;
+        return SDL.getContext();
     }
 
     static class ShowTextInputTask implements Runnable {
@@ -681,7 +647,7 @@ public class SDLActivity extends Activity {
             params.topMargin = y;
 
             if (mTextEdit == null) {
-                mTextEdit = new DummyEdit(getContext());
+                mTextEdit = new DummyEdit(SDL.getContext());
 
                 mLayout.addView(mTextEdit, params);
             } else {
@@ -691,7 +657,7 @@ public class SDLActivity extends Activity {
             mTextEdit.setVisibility(View.VISIBLE);
             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);
 
             mScreenKeyboardShown = true;
@@ -731,156 +697,6 @@ public class SDLActivity extends Activity {
         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
 
     /**
@@ -900,67 +716,20 @@ public class SDLActivity extends Activity {
         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
 
     /** 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. */
-    private Method expansionFileMethod;
+    private static Method expansionFileMethod;
 
     /**
      * This method is called by SDL using JNI.
      * @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.
      */
-    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
         if (expansionFile == null) {
             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
                 expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport")
                     .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
-                    .invoke(null, this, mainVersion, patchVersion);
+                    .invoke(null, SDL.getContext(), mainVersion, patchVersion);
 
                 expansionFileMethod = expansionFile.getClass()
                     .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
         // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
         // 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
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
+                if (SDLControllerManager.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
                     return true;
                 }
             } else if (event.getAction() == KeyEvent.ACTION_UP) {
-                if (SDLActivity.onNativePadUp(event.getDeviceId(), keyCode) == 0) {
+                if (SDLControllerManager.onNativePadUp(event.getDeviceId(), keyCode) == 0) {
                     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 {
 
     public boolean clipboardHasText();
@@ -2100,7 +1544,7 @@ class SDLClipboardHandler_API11 implements
     protected android.content.ClipboardManager mClipMgr;
 
     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);
     }
 
@@ -2139,7 +1583,7 @@ class SDLClipboardHandler_Old implements
     protected android.text.ClipboardManager mClipMgrOld;
   
     SDLClipboardHandler_Old() {
-       mClipMgrOld = (android.text.ClipboardManager) SDLActivity.mSingleton.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+       mClipMgrOld = (android.text.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
     }
 
     @Override
@@ -2164,4 +1608,3 @@ class SDLClipboardHandler_Old implements
     }
 }
 
-

+ 165 - 102
src/core/android/SDL_android.c

@@ -56,6 +56,8 @@
 #define CONCAT1(prefix, class, function)                CONCAT2(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_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)
 
 
@@ -75,39 +77,6 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
         JNIEnv* env, jclass jcls,
         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)(
         JNIEnv* env, jclass jcls);
 
@@ -166,6 +135,48 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingTex
         JNIEnv* env, jclass cls,
         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 */
 /* #define DEBUG_JNI */
@@ -189,17 +200,6 @@ static jclass mActivityClass;
 
 /* method signatures */
 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 midSetOrientation;
 static jmethodID midGetContext;
@@ -210,7 +210,28 @@ static jmethodID midIsScreenKeyboardShown;
 static jmethodID midClipboardSetText;
 static jmethodID midClipboardGetText;
 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 jfieldID fidSeparateMouseAndTouch;
@@ -245,7 +266,17 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
     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)
 {
     __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,
                                 "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,
                                 "setActivityTitle","(Ljava/lang/String;)Z");
     midSetOrientation = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
@@ -298,16 +307,14 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass c
                                 "clipboardGetText", "()Ljava/lang/String;");
     midClipboardHasText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
                                 "clipboardHasText", "()Z");
-
-    bHasNewData = SDL_FALSE;
+    midOpenAPKExpansionInputStream = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
 
     if (!midGetNativeSurface ||
-       !midAudioOpen || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioClose ||
-       !midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose ||
-       !midPollInputDevices || !midPollHapticDevices || !midHapticRun ||
        !midSetActivityTitle || !midSetOrientation || !midGetContext || !midInputGetInputDeviceIds ||
        !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?");
     }
 
@@ -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?");
     }
 
-    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 */
@@ -421,7 +485,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
 }
 
 /* Paddown */
-JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadDown)(
+JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
                                     JNIEnv* env, jclass jcls,
                                     jint device_id, jint keycode)
 {
@@ -429,7 +493,7 @@ JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadDown)(
 }
 
 /* Padup */
-JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadUp)(
+JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
                                     JNIEnv* env, jclass jcls,
                                     jint device_id, jint keycode)
 {
@@ -437,7 +501,7 @@ JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadUp)(
 }
 
 /* Joy */
-JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeJoy)(
+JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
                                     JNIEnv* env, jclass jcls,
                                     jint device_id, jint axis, jfloat value)
 {
@@ -445,7 +509,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeJoy)(
 }
 
 /* POV Hat */
-JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeHat)(
+JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
                                     JNIEnv* env, jclass jcls,
                                     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,
                                     jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
                                     jint nbuttons, jint naxes, jint nhats, jint nballs)
@@ -470,14 +534,14 @@ JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddJoystick)(
     return retval;
 }
 
-JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveJoystick)(
+JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
                                     JNIEnv* env, jclass jcls,
                                     jint 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)
 {
     int retval;
@@ -490,7 +554,7 @@ JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddHaptic)(
     return retval;
 }
 
-JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveHaptic)(
+JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
     JNIEnv* env, jclass jcls, jint device_id)
 {
     return Android_RemoveHaptic(device_id);
@@ -882,7 +946,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int
     if (iscapture) {
         __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
         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 */
             __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioRecord initialization!");
             return 0;
@@ -890,7 +954,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int
     } else {
         __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
         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 */
             __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
             return 0;
@@ -958,10 +1022,10 @@ void Android_JNI_WriteAudioBuffer(void)
 
     if (audioBuffer16Bit) {
         (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
-        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
+        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
     } else {
         (*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 */
@@ -975,7 +1039,7 @@ int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
 
     if (captureBuffer16Bit) {
         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) {
             jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
             br *= 2;
@@ -984,7 +1048,7 @@ int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
         }
     } else {
         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) {
             jbyte *ptr = (*env)->GetByteArrayElements(env, (jbyteArray)captureBuffer, &isCopy);
             SDL_memcpy(buffer, ptr, br);
@@ -1008,9 +1072,9 @@ void Android_JNI_FlushCapturedAudio(void)
     }
 #else
     if (captureBuffer16Bit) {
-        (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
+        (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
     } else {
-        (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
+        (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
     }
 #endif
 }
@@ -1020,13 +1084,13 @@ void Android_JNI_CloseAudioDevice(const int iscapture)
     JNIEnv *env = Android_JNI_GetEnv();
 
     if (iscapture) {
-        (*env)->CallStaticVoidMethod(env, mActivityClass, midCaptureClose);
+        (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midCaptureClose);
         if (captureBuffer) {
             (*env)->DeleteGlobalRef(env, captureBuffer);
             captureBuffer = NULL;
         }
     } else {
-        (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioClose);
+        (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioClose);
         if (audioBuffer) {
             (*env)->DeleteGlobalRef(env, audioBuffer);
             audioBuffer = NULL;
@@ -1160,13 +1224,7 @@ fallback:
         inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */);
         if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
             /* Try fallback to APK expansion files */
-            mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
-                "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);
+            inputStream = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, midOpenAPKExpansionInputStream, fileNameJString);
 
             /* Exception is checked first because it always needs to be cleared.
              * 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)
 {
     JNIEnv *env = Android_JNI_GetEnv();
-    (*env)->CallStaticVoidMethod(env, mActivityClass, midPollInputDevices);
+    (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices);
 }
 
 void Android_JNI_PollHapticDevices(void)
 {
     JNIEnv *env = Android_JNI_GetEnv();
-    (*env)->CallStaticVoidMethod(env, mActivityClass, midPollHapticDevices);
+    (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollHapticDevices);
 }
 
 void Android_JNI_HapticRun(int device_id, int length)
 {
     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 = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
+        if (!context) {
+            SDL_SetError("Couldn't get Android context!");
+            LocalReferenceHolder_Cleanup(&refs);
+            return NULL;
+        }
 
         /* fileObj = context.getFilesDir(); */
         mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),