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.
authorSam Lantinga <slouken@libsdl.org>
Fri, 22 Sep 2017 08:30:46 -0700
changeset 1153184793b17a0d2
parent 11530 14c4783c4143
child 11532 4af4e3986438
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.
android-project/src/org/libsdl/app/SDLActivity.java
src/core/android/SDL_android.c
     1.1 --- a/android-project/src/org/libsdl/app/SDLActivity.java	Fri Sep 22 08:30:37 2017 -0700
     1.2 +++ b/android-project/src/org/libsdl/app/SDLActivity.java	Fri Sep 22 08:30:46 2017 -0700
     1.3 @@ -56,24 +56,17 @@
     1.4      public static boolean mSeparateMouseAndTouch;
     1.5  
     1.6      // Main components
     1.7 -    protected static Context mContext;
     1.8      protected static SDLActivity mSingleton;
     1.9      protected static SDLSurface mSurface;
    1.10      protected static View mTextEdit;
    1.11      protected static boolean mScreenKeyboardShown;
    1.12      protected static ViewGroup mLayout;
    1.13 -    protected static SDLJoystickHandler mJoystickHandler;
    1.14 -    protected static SDLHapticHandler mHapticHandler;
    1.15      protected static SDLClipboardHandler mClipboardHandler;
    1.16  
    1.17  
    1.18      // This is what SDL runs in. It invokes SDL_main(), eventually
    1.19      protected static Thread mSDLThread;
    1.20  
    1.21 -    // Audio
    1.22 -    protected static AudioTrack mAudioTrack;
    1.23 -    protected static AudioRecord mAudioRecord;
    1.24 -
    1.25      /**
    1.26       * This method returns the name of the shared object with the application entry point
    1.27       * It can be overridden by derived classes.
    1.28 @@ -136,17 +129,12 @@
    1.29      public static void initialize() {
    1.30          // The static nature of the singleton and Android quirkyness force us to initialize everything here
    1.31          // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
    1.32 -        mContext = null;
    1.33          mSingleton = null;
    1.34          mSurface = null;
    1.35          mTextEdit = null;
    1.36          mLayout = null;
    1.37 -        mJoystickHandler = null;
    1.38 -        mHapticHandler = null;
    1.39          mClipboardHandler = null;
    1.40          mSDLThread = null;
    1.41 -        mAudioTrack = null;
    1.42 -        mAudioRecord = null;
    1.43          mExitCalledFromJava = false;
    1.44          mBrokenLibraries = false;
    1.45          mIsResumedCalled = false;
    1.46 @@ -157,9 +145,6 @@
    1.47      }
    1.48  
    1.49      // Setup
    1.50 -    public static void setContext(Context context) {
    1.51 -        mContext = context;
    1.52 -    }
    1.53      @Override
    1.54      protected void onCreate(Bundle savedInstanceState) {
    1.55          Log.v(TAG, "Device: " + android.os.Build.DEVICE);
    1.56 @@ -167,11 +152,6 @@
    1.57          Log.v(TAG, "onCreate()");
    1.58          super.onCreate(savedInstanceState);
    1.59  
    1.60 -        SDLActivity.initialize();
    1.61 -
    1.62 -        // So we can call stuff from static callbacks
    1.63 -        mContext = mSingleton = this;
    1.64 -
    1.65          // Load shared libraries
    1.66          String errorMsgBrokenLib = "";
    1.67          try {
    1.68 @@ -209,16 +189,14 @@
    1.69          }
    1.70  
    1.71          // Set up JNI
    1.72 -        SDLActivity.nativeSetupJNI();
    1.73 +        SDL.setupJNI();
    1.74  
    1.75 -        if (Build.VERSION.SDK_INT >= 16) {
    1.76 -            mJoystickHandler = new SDLJoystickHandler_API16();
    1.77 -        } else if (Build.VERSION.SDK_INT >= 12) {
    1.78 -            mJoystickHandler = new SDLJoystickHandler_API12();
    1.79 -        } else {
    1.80 -            mJoystickHandler = new SDLJoystickHandler();
    1.81 -        }
    1.82 -        mHapticHandler = new SDLHapticHandler();
    1.83 +        // Initialize state
    1.84 +        SDL.initialize();
    1.85 +
    1.86 +        // So we can call stuff from static callbacks
    1.87 +        mSingleton = this;
    1.88 +        SDL.setContext(this);
    1.89  
    1.90          if (Build.VERSION.SDK_INT >= 11) {
    1.91              mClipboardHandler = new SDLClipboardHandler_API11();
    1.92 @@ -461,7 +439,7 @@
    1.93      protected static class SDLCommandHandler extends Handler {
    1.94          @Override
    1.95          public void handleMessage(Message msg) {
    1.96 -            Context context = getContext();
    1.97 +            Context context = SDL.getContext();
    1.98              if (context == null) {
    1.99                  Log.e(TAG, "error handling message, getContext() returned null");
   1.100                  return;
   1.101 @@ -529,12 +507,6 @@
   1.102      public static native void nativeResume();
   1.103      public static native void onNativeDropFile(String filename);
   1.104      public static native void onNativeResize(int x, int y, int format, float rate);
   1.105 -    public static native int onNativePadDown(int device_id, int keycode);
   1.106 -    public static native int onNativePadUp(int device_id, int keycode);
   1.107 -    public static native void onNativeJoy(int device_id, int axis,
   1.108 -                                          float value);
   1.109 -    public static native void onNativeHat(int device_id, int hat_id,
   1.110 -                                          int x, int y);
   1.111      public static native void onNativeKeyDown(int keycode);
   1.112      public static native void onNativeKeyUp(int keycode);
   1.113      public static native void onNativeKeyboardFocusLost();
   1.114 @@ -546,12 +518,6 @@
   1.115      public static native void onNativeClipboardChanged();
   1.116      public static native void onNativeSurfaceChanged();
   1.117      public static native void onNativeSurfaceDestroyed();
   1.118 -    public static native int nativeAddJoystick(int device_id, String name, String desc,
   1.119 -                                               int is_accelerometer, int nbuttons,
   1.120 -                                               int naxes, int nhats, int nballs);
   1.121 -    public static native int nativeRemoveJoystick(int device_id);
   1.122 -    public static native int nativeAddHaptic(int device_id, String name);
   1.123 -    public static native int nativeRemoveHaptic(int device_id);
   1.124      public static native String nativeGetHint(String name);
   1.125  
   1.126      /**
   1.127 @@ -632,7 +598,7 @@
   1.128            return false;
   1.129         }
   1.130  
   1.131 -       InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
   1.132 +       InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
   1.133         if (imm.isAcceptingText()) {
   1.134            return true;
   1.135         }
   1.136 @@ -654,7 +620,7 @@
   1.137       * This method is called by SDL using JNI.
   1.138       */
   1.139      public static Context getContext() {
   1.140 -        return mContext;
   1.141 +        return SDL.getContext();
   1.142      }
   1.143  
   1.144      static class ShowTextInputTask implements Runnable {
   1.145 @@ -681,7 +647,7 @@
   1.146              params.topMargin = y;
   1.147  
   1.148              if (mTextEdit == null) {
   1.149 -                mTextEdit = new DummyEdit(getContext());
   1.150 +                mTextEdit = new DummyEdit(SDL.getContext());
   1.151  
   1.152                  mLayout.addView(mTextEdit, params);
   1.153              } else {
   1.154 @@ -691,7 +657,7 @@
   1.155              mTextEdit.setVisibility(View.VISIBLE);
   1.156              mTextEdit.requestFocus();
   1.157  
   1.158 -            InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
   1.159 +            InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
   1.160              imm.showSoftInput(mTextEdit, 0);
   1.161  
   1.162              mScreenKeyboardShown = true;
   1.163 @@ -731,156 +697,6 @@
   1.164          return SDLActivity.mSurface.getNativeSurface();
   1.165      }
   1.166  
   1.167 -    // Audio
   1.168 -
   1.169 -    /**
   1.170 -     * This method is called by SDL using JNI.
   1.171 -     */
   1.172 -    public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
   1.173 -        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
   1.174 -        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
   1.175 -        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
   1.176 -
   1.177 -        Log.v(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
   1.178 -
   1.179 -        // Let the user pick a larger buffer if they really want -- but ye
   1.180 -        // gods they probably shouldn't, the minimums are horrifyingly high
   1.181 -        // latency already
   1.182 -        desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
   1.183 -
   1.184 -        if (mAudioTrack == null) {
   1.185 -            mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
   1.186 -                    channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
   1.187 -
   1.188 -            // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
   1.189 -            // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
   1.190 -            // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
   1.191 -
   1.192 -            if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
   1.193 -                Log.e(TAG, "Failed during initialization of Audio Track");
   1.194 -                mAudioTrack = null;
   1.195 -                return -1;
   1.196 -            }
   1.197 -
   1.198 -            mAudioTrack.play();
   1.199 -        }
   1.200 -
   1.201 -        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");
   1.202 -
   1.203 -        return 0;
   1.204 -    }
   1.205 -
   1.206 -    /**
   1.207 -     * This method is called by SDL using JNI.
   1.208 -     */
   1.209 -    public static void audioWriteShortBuffer(short[] buffer) {
   1.210 -        for (int i = 0; i < buffer.length; ) {
   1.211 -            int result = mAudioTrack.write(buffer, i, buffer.length - i);
   1.212 -            if (result > 0) {
   1.213 -                i += result;
   1.214 -            } else if (result == 0) {
   1.215 -                try {
   1.216 -                    Thread.sleep(1);
   1.217 -                } catch(InterruptedException e) {
   1.218 -                    // Nom nom
   1.219 -                }
   1.220 -            } else {
   1.221 -                Log.w(TAG, "SDL audio: error return from write(short)");
   1.222 -                return;
   1.223 -            }
   1.224 -        }
   1.225 -    }
   1.226 -
   1.227 -    /**
   1.228 -     * This method is called by SDL using JNI.
   1.229 -     */
   1.230 -    public static void audioWriteByteBuffer(byte[] buffer) {
   1.231 -        for (int i = 0; i < buffer.length; ) {
   1.232 -            int result = mAudioTrack.write(buffer, i, buffer.length - i);
   1.233 -            if (result > 0) {
   1.234 -                i += result;
   1.235 -            } else if (result == 0) {
   1.236 -                try {
   1.237 -                    Thread.sleep(1);
   1.238 -                } catch(InterruptedException e) {
   1.239 -                    // Nom nom
   1.240 -                }
   1.241 -            } else {
   1.242 -                Log.w(TAG, "SDL audio: error return from write(byte)");
   1.243 -                return;
   1.244 -            }
   1.245 -        }
   1.246 -    }
   1.247 -
   1.248 -    /**
   1.249 -     * This method is called by SDL using JNI.
   1.250 -     */
   1.251 -    public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
   1.252 -        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
   1.253 -        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
   1.254 -        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
   1.255 -
   1.256 -        Log.v(TAG, "SDL capture: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
   1.257 -
   1.258 -        // Let the user pick a larger buffer if they really want -- but ye
   1.259 -        // gods they probably shouldn't, the minimums are horrifyingly high
   1.260 -        // latency already
   1.261 -        desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
   1.262 -
   1.263 -        if (mAudioRecord == null) {
   1.264 -            mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
   1.265 -                    channelConfig, audioFormat, desiredFrames * frameSize);
   1.266 -
   1.267 -            // see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
   1.268 -            if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
   1.269 -                Log.e(TAG, "Failed during initialization of AudioRecord");
   1.270 -                mAudioRecord.release();
   1.271 -                mAudioRecord = null;
   1.272 -                return -1;
   1.273 -            }
   1.274 -
   1.275 -            mAudioRecord.startRecording();
   1.276 -        }
   1.277 -
   1.278 -        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");
   1.279 -
   1.280 -        return 0;
   1.281 -    }
   1.282 -
   1.283 -    /** This method is called by SDL using JNI. */
   1.284 -    public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
   1.285 -        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
   1.286 -        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
   1.287 -        return mAudioRecord.read(buffer, 0, buffer.length);
   1.288 -    }
   1.289 -
   1.290 -    /** This method is called by SDL using JNI. */
   1.291 -    public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
   1.292 -        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
   1.293 -        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
   1.294 -        return mAudioRecord.read(buffer, 0, buffer.length);
   1.295 -    }
   1.296 -
   1.297 -
   1.298 -    /** This method is called by SDL using JNI. */
   1.299 -    public static void audioClose() {
   1.300 -        if (mAudioTrack != null) {
   1.301 -            mAudioTrack.stop();
   1.302 -            mAudioTrack.release();
   1.303 -            mAudioTrack = null;
   1.304 -        }
   1.305 -    }
   1.306 -
   1.307 -    /** This method is called by SDL using JNI. */
   1.308 -    public static void captureClose() {
   1.309 -        if (mAudioRecord != null) {
   1.310 -            mAudioRecord.stop();
   1.311 -            mAudioRecord.release();
   1.312 -            mAudioRecord = null;
   1.313 -        }
   1.314 -    }
   1.315 -
   1.316 -
   1.317      // Input
   1.318  
   1.319      /**
   1.320 @@ -900,67 +716,20 @@
   1.321          return Arrays.copyOf(filtered, used);
   1.322      }
   1.323  
   1.324 -    // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
   1.325 -    public static boolean handleJoystickMotionEvent(MotionEvent event) {
   1.326 -        return mJoystickHandler.handleMotionEvent(event);
   1.327 -    }
   1.328 -
   1.329 -    /**
   1.330 -     * This method is called by SDL using JNI.
   1.331 -     */
   1.332 -    public static void pollInputDevices() {
   1.333 -        if (SDLActivity.mSDLThread != null) {
   1.334 -            mJoystickHandler.pollInputDevices();
   1.335 -        }
   1.336 -    }
   1.337 -
   1.338 -    /**
   1.339 -     * This method is called by SDL using JNI.
   1.340 -     */
   1.341 -    public static void pollHapticDevices() {
   1.342 -        if (SDLActivity.mSDLThread != null) {
   1.343 -            mHapticHandler.pollHapticDevices();
   1.344 -        }
   1.345 -    }
   1.346 -
   1.347 -    /**
   1.348 -     * This method is called by SDL using JNI.
   1.349 -     */
   1.350 -    public static void hapticRun(int device_id, int length) {
   1.351 -        if (SDLActivity.mSDLThread != null) {
   1.352 -            mHapticHandler.run(device_id, length);
   1.353 -        }
   1.354 -    }
   1.355 -
   1.356 -    // Check if a given device is considered a possible SDL joystick
   1.357 -    public static boolean isDeviceSDLJoystick(int deviceId) {
   1.358 -        InputDevice device = InputDevice.getDevice(deviceId);
   1.359 -        // We cannot use InputDevice.isVirtual before API 16, so let's accept
   1.360 -        // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
   1.361 -        if ((device == null) || (deviceId < 0)) {
   1.362 -            return false;
   1.363 -        }
   1.364 -        int sources = device.getSources();
   1.365 -        return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
   1.366 -                ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
   1.367 -                ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
   1.368 -        );
   1.369 -    }
   1.370 -
   1.371      // APK expansion files support
   1.372  
   1.373      /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
   1.374 -    private Object expansionFile;
   1.375 +    private static Object expansionFile;
   1.376  
   1.377      /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */
   1.378 -    private Method expansionFileMethod;
   1.379 +    private static Method expansionFileMethod;
   1.380  
   1.381      /**
   1.382       * This method is called by SDL using JNI.
   1.383       * @return an InputStream on success or null if no expansion file was used.
   1.384       * @throws IOException on errors. Message is set for the SDL error message.
   1.385       */
   1.386 -    public InputStream openAPKExpansionInputStream(String fileName) throws IOException {
   1.387 +    public static InputStream openAPKExpansionInputStream(String fileName) throws IOException {
   1.388          // Get a ZipResourceFile representing a merger of both the main and patch files
   1.389          if (expansionFile == null) {
   1.390              String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION");
   1.391 @@ -987,7 +756,7 @@
   1.392                  // not a part of Android SDK we access it using reflection
   1.393                  expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport")
   1.394                      .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
   1.395 -                    .invoke(null, this, mainVersion, patchVersion);
   1.396 +                    .invoke(null, SDL.getContext(), mainVersion, patchVersion);
   1.397  
   1.398                  expansionFileMethod = expansionFile.getClass()
   1.399                      .getMethod("getInputStream", String.class);
   1.400 @@ -1452,14 +1221,14 @@
   1.401          // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
   1.402          // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
   1.403          // So, retrieve the device itself and check all of its sources
   1.404 -        if (SDLActivity.isDeviceSDLJoystick(event.getDeviceId())) {
   1.405 +        if (SDLControllerManager.isDeviceSDLJoystick(event.getDeviceId())) {
   1.406              // Note that we process events with specific key codes here
   1.407              if (event.getAction() == KeyEvent.ACTION_DOWN) {
   1.408 -                if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
   1.409 +                if (SDLControllerManager.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
   1.410                      return true;
   1.411                  }
   1.412              } else if (event.getAction() == KeyEvent.ACTION_UP) {
   1.413 -                if (SDLActivity.onNativePadUp(event.getDeviceId(), keyCode) == 0) {
   1.414 +                if (SDLControllerManager.onNativePadUp(event.getDeviceId(), keyCode) == 0) {
   1.415                      return true;
   1.416                  }
   1.417              }
   1.418 @@ -1759,331 +1528,6 @@
   1.419      }
   1.420  }
   1.421  
   1.422 -/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
   1.423 -class SDLJoystickHandler {
   1.424 -
   1.425 -    /**
   1.426 -     * Handles given MotionEvent.
   1.427 -     * @param event the event to be handled.
   1.428 -     * @return if given event was processed.
   1.429 -     */
   1.430 -    public boolean handleMotionEvent(MotionEvent event) {
   1.431 -        return false;
   1.432 -    }
   1.433 -
   1.434 -    /**
   1.435 -     * Handles adding and removing of input devices.
   1.436 -     */
   1.437 -    public void pollInputDevices() {
   1.438 -    }
   1.439 -}
   1.440 -
   1.441 -/* Actual joystick functionality available for API >= 12 devices */
   1.442 -class SDLJoystickHandler_API12 extends SDLJoystickHandler {
   1.443 -
   1.444 -    static class SDLJoystick {
   1.445 -        public int device_id;
   1.446 -        public String name;
   1.447 -        public String desc;
   1.448 -        public ArrayList<InputDevice.MotionRange> axes;
   1.449 -        public ArrayList<InputDevice.MotionRange> hats;
   1.450 -    }
   1.451 -    static class RangeComparator implements Comparator<InputDevice.MotionRange> {
   1.452 -        @Override
   1.453 -        public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
   1.454 -            return arg0.getAxis() - arg1.getAxis();
   1.455 -        }
   1.456 -    }
   1.457 -
   1.458 -    private ArrayList<SDLJoystick> mJoysticks;
   1.459 -
   1.460 -    public SDLJoystickHandler_API12() {
   1.461 -
   1.462 -        mJoysticks = new ArrayList<SDLJoystick>();
   1.463 -    }
   1.464 -
   1.465 -    @Override
   1.466 -    public void pollInputDevices() {
   1.467 -        int[] deviceIds = InputDevice.getDeviceIds();
   1.468 -        // It helps processing the device ids in reverse order
   1.469 -        // For example, in the case of the XBox 360 wireless dongle,
   1.470 -        // so the first controller seen by SDL matches what the receiver
   1.471 -        // considers to be the first controller
   1.472 -
   1.473 -        for(int i=deviceIds.length-1; i>-1; i--) {
   1.474 -            SDLJoystick joystick = getJoystick(deviceIds[i]);
   1.475 -            if (joystick == null) {
   1.476 -                joystick = new SDLJoystick();
   1.477 -                InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
   1.478 -                if (SDLActivity.isDeviceSDLJoystick(deviceIds[i])) {
   1.479 -                    joystick.device_id = deviceIds[i];
   1.480 -                    joystick.name = joystickDevice.getName();
   1.481 -                    joystick.desc = getJoystickDescriptor(joystickDevice);
   1.482 -                    joystick.axes = new ArrayList<InputDevice.MotionRange>();
   1.483 -                    joystick.hats = new ArrayList<InputDevice.MotionRange>();
   1.484 -
   1.485 -                    List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
   1.486 -                    Collections.sort(ranges, new RangeComparator());
   1.487 -                    for (InputDevice.MotionRange range : ranges ) {
   1.488 -                        if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
   1.489 -                            if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
   1.490 -                                range.getAxis() == MotionEvent.AXIS_HAT_Y) {
   1.491 -                                joystick.hats.add(range);
   1.492 -                            }
   1.493 -                            else {
   1.494 -                                joystick.axes.add(range);
   1.495 -                            }
   1.496 -                        }
   1.497 -                    }
   1.498 -
   1.499 -                    mJoysticks.add(joystick);
   1.500 -                    SDLActivity.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1,
   1.501 -                                                  joystick.axes.size(), joystick.hats.size()/2, 0);
   1.502 -                }
   1.503 -            }
   1.504 -        }
   1.505 -
   1.506 -        /* Check removed devices */
   1.507 -        ArrayList<Integer> removedDevices = new ArrayList<Integer>();
   1.508 -        for(int i=0; i < mJoysticks.size(); i++) {
   1.509 -            int device_id = mJoysticks.get(i).device_id;
   1.510 -            int j;
   1.511 -            for (j=0; j < deviceIds.length; j++) {
   1.512 -                if (device_id == deviceIds[j]) break;
   1.513 -            }
   1.514 -            if (j == deviceIds.length) {
   1.515 -                removedDevices.add(Integer.valueOf(device_id));
   1.516 -            }
   1.517 -        }
   1.518 -
   1.519 -        for(int i=0; i < removedDevices.size(); i++) {
   1.520 -            int device_id = removedDevices.get(i).intValue();
   1.521 -            SDLActivity.nativeRemoveJoystick(device_id);
   1.522 -            for (int j=0; j < mJoysticks.size(); j++) {
   1.523 -                if (mJoysticks.get(j).device_id == device_id) {
   1.524 -                    mJoysticks.remove(j);
   1.525 -                    break;
   1.526 -                }
   1.527 -            }
   1.528 -        }
   1.529 -    }
   1.530 -
   1.531 -    protected SDLJoystick getJoystick(int device_id) {
   1.532 -        for(int i=0; i < mJoysticks.size(); i++) {
   1.533 -            if (mJoysticks.get(i).device_id == device_id) {
   1.534 -                return mJoysticks.get(i);
   1.535 -            }
   1.536 -        }
   1.537 -        return null;
   1.538 -    }
   1.539 -
   1.540 -    @Override
   1.541 -    public boolean handleMotionEvent(MotionEvent event) {
   1.542 -        if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
   1.543 -            int actionPointerIndex = event.getActionIndex();
   1.544 -            int action = event.getActionMasked();
   1.545 -            switch(action) {
   1.546 -                case MotionEvent.ACTION_MOVE:
   1.547 -                    SDLJoystick joystick = getJoystick(event.getDeviceId());
   1.548 -                    if ( joystick != null ) {
   1.549 -                        for (int i = 0; i < joystick.axes.size(); i++) {
   1.550 -                            InputDevice.MotionRange range = joystick.axes.get(i);
   1.551 -                            /* Normalize the value to -1...1 */
   1.552 -                            float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
   1.553 -                            SDLActivity.onNativeJoy(joystick.device_id, i, value );
   1.554 -                        }
   1.555 -                        for (int i = 0; i < joystick.hats.size(); i+=2) {
   1.556 -                            int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
   1.557 -                            int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
   1.558 -                            SDLActivity.onNativeHat(joystick.device_id, i/2, hatX, hatY );
   1.559 -                        }
   1.560 -                    }
   1.561 -                    break;
   1.562 -                default:
   1.563 -                    break;
   1.564 -            }
   1.565 -        }
   1.566 -        return true;
   1.567 -    }
   1.568 -
   1.569 -    public String getJoystickDescriptor(InputDevice joystickDevice) {
   1.570 -        return joystickDevice.getName();
   1.571 -    }
   1.572 -}
   1.573 -
   1.574 -
   1.575 -class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
   1.576 -
   1.577 -    @Override
   1.578 -    public String getJoystickDescriptor(InputDevice joystickDevice) {
   1.579 -        String desc = joystickDevice.getDescriptor();
   1.580 -
   1.581 -        if (desc != null && desc != "") {
   1.582 -            return desc;
   1.583 -        }
   1.584 -
   1.585 -        return super.getJoystickDescriptor(joystickDevice);
   1.586 -    }
   1.587 -}
   1.588 -
   1.589 -class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
   1.590 -    // Generic Motion (mouse hover, joystick...) events go here
   1.591 -    @Override
   1.592 -    public boolean onGenericMotion(View v, MotionEvent event) {
   1.593 -        float x, y;
   1.594 -        int action;
   1.595 -
   1.596 -        switch ( event.getSource() ) {
   1.597 -            case InputDevice.SOURCE_JOYSTICK:
   1.598 -            case InputDevice.SOURCE_GAMEPAD:
   1.599 -            case InputDevice.SOURCE_DPAD:
   1.600 -                return SDLActivity.handleJoystickMotionEvent(event);
   1.601 -
   1.602 -            case InputDevice.SOURCE_MOUSE:
   1.603 -                if (!SDLActivity.mSeparateMouseAndTouch) {
   1.604 -                    break;
   1.605 -                }
   1.606 -                action = event.getActionMasked();
   1.607 -                switch (action) {
   1.608 -                    case MotionEvent.ACTION_SCROLL:
   1.609 -                        x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
   1.610 -                        y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
   1.611 -                        SDLActivity.onNativeMouse(0, action, x, y);
   1.612 -                        return true;
   1.613 -
   1.614 -                    case MotionEvent.ACTION_HOVER_MOVE:
   1.615 -                        x = event.getX(0);
   1.616 -                        y = event.getY(0);
   1.617 -
   1.618 -                        SDLActivity.onNativeMouse(0, action, x, y);
   1.619 -                        return true;
   1.620 -
   1.621 -                    default:
   1.622 -                        break;
   1.623 -                }
   1.624 -                break;
   1.625 -
   1.626 -            default:
   1.627 -                break;
   1.628 -        }
   1.629 -
   1.630 -        // Event was not managed
   1.631 -        return false;
   1.632 -    }
   1.633 -}
   1.634 -
   1.635 -class SDLHapticHandler {
   1.636 -
   1.637 -    class SDLHaptic {
   1.638 -        public int device_id;
   1.639 -        public String name;
   1.640 -        public Vibrator vib;
   1.641 -    }
   1.642 -
   1.643 -    private ArrayList<SDLHaptic> mHaptics;
   1.644 -    
   1.645 -    public SDLHapticHandler() {
   1.646 -        mHaptics = new ArrayList<SDLHaptic>();
   1.647 -    }
   1.648 -
   1.649 -    public void run(int device_id, int length) {
   1.650 -        SDLHaptic haptic = getHaptic(device_id);
   1.651 -        if (haptic != null) {
   1.652 -            haptic.vib.vibrate (length);
   1.653 -        }
   1.654 -    }
   1.655 -
   1.656 -    public void pollHapticDevices() {
   1.657 -
   1.658 -        final int deviceId_VIBRATOR_SERVICE = 999999;
   1.659 -        boolean hasVibratorService = false;
   1.660 -
   1.661 -        int[] deviceIds = InputDevice.getDeviceIds();
   1.662 -        // It helps processing the device ids in reverse order
   1.663 -        // For example, in the case of the XBox 360 wireless dongle,
   1.664 -        // so the first controller seen by SDL matches what the receiver
   1.665 -        // considers to be the first controller
   1.666 -
   1.667 -        if (Build.VERSION.SDK_INT >= 16)
   1.668 -        {
   1.669 -            for (int i = deviceIds.length-1; i > -1; i--) {
   1.670 -                SDLHaptic haptic = getHaptic(deviceIds[i]);
   1.671 -                if (haptic == null) {
   1.672 -                    InputDevice device = InputDevice.getDevice(deviceIds[i]);
   1.673 -                    Vibrator vib = device.getVibrator();
   1.674 -                    if (vib.hasVibrator()) {
   1.675 -                        haptic = new SDLHaptic();
   1.676 -                        haptic.device_id = deviceIds[i];
   1.677 -                        haptic.name = device.getName();
   1.678 -                        haptic.vib = vib;
   1.679 -                        mHaptics.add(haptic);
   1.680 -                        SDLActivity.nativeAddHaptic(haptic.device_id, haptic.name);
   1.681 -                    }
   1.682 -                }
   1.683 -            }
   1.684 -        }
   1.685 -
   1.686 -        /* Check VIBRATOR_SERVICE */
   1.687 -        Vibrator vib = (Vibrator) SDLActivity.mSingleton.getContext().getSystemService(Context.VIBRATOR_SERVICE);
   1.688 -        if (vib != null) {
   1.689 -            if (Build.VERSION.SDK_INT >= 11) {
   1.690 -                hasVibratorService = vib.hasVibrator();
   1.691 -            } else {
   1.692 -                hasVibratorService = true;
   1.693 -            }
   1.694 -
   1.695 -            if (hasVibratorService) {
   1.696 -                SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
   1.697 -                if (haptic == null) {
   1.698 -                    haptic = new SDLHaptic();
   1.699 -                    haptic.device_id = deviceId_VIBRATOR_SERVICE;
   1.700 -                    haptic.name = "VIBRATOR_SERVICE";
   1.701 -                    haptic.vib = vib; 
   1.702 -                    mHaptics.add(haptic);
   1.703 -                    SDLActivity.nativeAddHaptic(haptic.device_id, haptic.name);
   1.704 -                }
   1.705 -            }
   1.706 -        }
   1.707 -
   1.708 -        /* Check removed devices */
   1.709 -        ArrayList<Integer> removedDevices = new ArrayList<Integer>();
   1.710 -        for(int i=0; i < mHaptics.size(); i++) {
   1.711 -            int device_id = mHaptics.get(i).device_id;
   1.712 -            int j;
   1.713 -            for (j=0; j < deviceIds.length; j++) {
   1.714 -                if (device_id == deviceIds[j]) break;
   1.715 -            }
   1.716 -
   1.717 -            if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) {
   1.718 -                // don't remove the vibrator if it is still present
   1.719 -            } else if (j == deviceIds.length) {
   1.720 -                removedDevices.add(device_id);
   1.721 -            }
   1.722 -        }
   1.723 -
   1.724 -        for(int i=0; i < removedDevices.size(); i++) {
   1.725 -            int device_id = removedDevices.get(i);
   1.726 -            SDLActivity.nativeRemoveHaptic(device_id);
   1.727 -            for (int j=0; j < mHaptics.size(); j++) {
   1.728 -                if (mHaptics.get(j).device_id == device_id) {
   1.729 -                    mHaptics.remove(j);
   1.730 -                    break;
   1.731 -                }
   1.732 -            }
   1.733 -        }
   1.734 -    }
   1.735 -
   1.736 -    protected SDLHaptic getHaptic(int device_id) {
   1.737 -        for(int i=0; i < mHaptics.size(); i++) {
   1.738 -            if (mHaptics.get(i).device_id == device_id) {
   1.739 -                return mHaptics.get(i);
   1.740 -            }
   1.741 -        }
   1.742 -        return null;
   1.743 -    }   
   1.744 -}
   1.745 -
   1.746 -
   1.747  interface SDLClipboardHandler {
   1.748  
   1.749      public boolean clipboardHasText();
   1.750 @@ -2100,7 +1544,7 @@
   1.751      protected android.content.ClipboardManager mClipMgr;
   1.752  
   1.753      SDLClipboardHandler_API11() {
   1.754 -       mClipMgr = (android.content.ClipboardManager) SDLActivity.mSingleton.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
   1.755 +       mClipMgr = (android.content.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
   1.756         mClipMgr.addPrimaryClipChangedListener(this);
   1.757      }
   1.758  
   1.759 @@ -2139,7 +1583,7 @@
   1.760      protected android.text.ClipboardManager mClipMgrOld;
   1.761    
   1.762      SDLClipboardHandler_Old() {
   1.763 -       mClipMgrOld = (android.text.ClipboardManager) SDLActivity.mSingleton.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
   1.764 +       mClipMgrOld = (android.text.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
   1.765      }
   1.766  
   1.767      @Override
   1.768 @@ -2164,4 +1608,3 @@
   1.769      }
   1.770  }
   1.771  
   1.772 -
     2.1 --- a/src/core/android/SDL_android.c	Fri Sep 22 08:30:37 2017 -0700
     2.2 +++ b/src/core/android/SDL_android.c	Fri Sep 22 08:30:46 2017 -0700
     2.3 @@ -56,6 +56,8 @@
     2.4  #define CONCAT1(prefix, class, function)                CONCAT2(prefix, class, function)
     2.5  #define CONCAT2(prefix, class, function)                Java_ ## prefix ## _ ## class ## _ ## function
     2.6  #define SDL_JAVA_INTERFACE(function)                    CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function)
     2.7 +#define SDL_JAVA_AUDIO_INTERFACE(function)              CONCAT1(SDL_JAVA_PREFIX, SDLAudioManager, function)
     2.8 +#define SDL_JAVA_CONTROLLER_INTERFACE(function)         CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
     2.9  #define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function)   CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
    2.10  
    2.11  
    2.12 @@ -75,39 +77,6 @@
    2.13          JNIEnv* env, jclass jcls,
    2.14          jint width, jint height, jint format, jfloat rate);
    2.15  
    2.16 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadDown)(
    2.17 -        JNIEnv* env, jclass jcls,
    2.18 -        jint device_id, jint keycode);
    2.19 -
    2.20 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadUp)(
    2.21 -        JNIEnv* env, jclass jcls,
    2.22 -        jint device_id, jint keycode);
    2.23 -
    2.24 -JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeJoy)(
    2.25 -        JNIEnv* env, jclass jcls,
    2.26 -        jint device_id, jint axis, jfloat value);
    2.27 -
    2.28 -JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeHat)(
    2.29 -        JNIEnv* env, jclass jcls,
    2.30 -        jint device_id, jint hat_id, jint x, jint y);
    2.31 -
    2.32 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddJoystick)(
    2.33 -        JNIEnv* env, jclass jcls,
    2.34 -        jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
    2.35 -        jint nbuttons, jint naxes, jint nhats, jint nballs);
    2.36 -
    2.37 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveJoystick)(
    2.38 -        JNIEnv* env, jclass jcls,
    2.39 -        jint device_id);
    2.40 -
    2.41 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddHaptic)(
    2.42 -        JNIEnv* env, jclass jcls,
    2.43 -        jint device_id, jstring device_name);
    2.44 -
    2.45 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveHaptic)(
    2.46 -        JNIEnv* env, jclass jcls,
    2.47 -        jint device_id);
    2.48 -
    2.49  JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(
    2.50          JNIEnv* env, jclass jcls);
    2.51  
    2.52 @@ -166,6 +135,48 @@
    2.53          JNIEnv* env, jclass cls,
    2.54          jstring text, jint newCursorPosition);
    2.55  
    2.56 +/* Java class SDLAudioManager */
    2.57 +JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
    2.58 +        JNIEnv *env, jclass jcls);
    2.59 +
    2.60 +/* Java class SDLControllerManager */
    2.61 +JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(
    2.62 +        JNIEnv *env, jclass jcls);
    2.63 +
    2.64 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
    2.65 +        JNIEnv* env, jclass jcls,
    2.66 +        jint device_id, jint keycode);
    2.67 +
    2.68 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
    2.69 +        JNIEnv* env, jclass jcls,
    2.70 +        jint device_id, jint keycode);
    2.71 +
    2.72 +JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
    2.73 +        JNIEnv* env, jclass jcls,
    2.74 +        jint device_id, jint axis, jfloat value);
    2.75 +
    2.76 +JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
    2.77 +        JNIEnv* env, jclass jcls,
    2.78 +        jint device_id, jint hat_id, jint x, jint y);
    2.79 +
    2.80 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
    2.81 +        JNIEnv* env, jclass jcls,
    2.82 +        jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
    2.83 +        jint nbuttons, jint naxes, jint nhats, jint nballs);
    2.84 +
    2.85 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
    2.86 +        JNIEnv* env, jclass jcls,
    2.87 +        jint device_id);
    2.88 +
    2.89 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
    2.90 +        JNIEnv* env, jclass jcls,
    2.91 +        jint device_id, jstring device_name);
    2.92 +
    2.93 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
    2.94 +        JNIEnv* env, jclass jcls,
    2.95 +        jint device_id);
    2.96 +
    2.97 +
    2.98  
    2.99  /* Uncomment this to log messages entering and exiting methods in this file */
   2.100  /* #define DEBUG_JNI */
   2.101 @@ -189,17 +200,6 @@
   2.102  
   2.103  /* method signatures */
   2.104  static jmethodID midGetNativeSurface;
   2.105 -static jmethodID midAudioOpen;
   2.106 -static jmethodID midAudioWriteShortBuffer;
   2.107 -static jmethodID midAudioWriteByteBuffer;
   2.108 -static jmethodID midAudioClose;
   2.109 -static jmethodID midCaptureOpen;
   2.110 -static jmethodID midCaptureReadShortBuffer;
   2.111 -static jmethodID midCaptureReadByteBuffer;
   2.112 -static jmethodID midCaptureClose;
   2.113 -static jmethodID midPollInputDevices;
   2.114 -static jmethodID midPollHapticDevices;
   2.115 -static jmethodID midHapticRun;
   2.116  static jmethodID midSetActivityTitle;
   2.117  static jmethodID midSetOrientation;
   2.118  static jmethodID midGetContext;
   2.119 @@ -210,7 +210,28 @@
   2.120  static jmethodID midClipboardSetText;
   2.121  static jmethodID midClipboardGetText;
   2.122  static jmethodID midClipboardHasText;
   2.123 +static jmethodID midOpenAPKExpansionInputStream;
   2.124  
   2.125 +/* audio manager */
   2.126 +static jclass mAudioManagerClass;
   2.127 +
   2.128 +/* method signatures */
   2.129 +static jmethodID midAudioOpen;
   2.130 +static jmethodID midAudioWriteShortBuffer;
   2.131 +static jmethodID midAudioWriteByteBuffer;
   2.132 +static jmethodID midAudioClose;
   2.133 +static jmethodID midCaptureOpen;
   2.134 +static jmethodID midCaptureReadShortBuffer;
   2.135 +static jmethodID midCaptureReadByteBuffer;
   2.136 +static jmethodID midCaptureClose;
   2.137 +
   2.138 +/* controller manager */
   2.139 +static jclass mControllerManagerClass;
   2.140 +
   2.141 +/* method signatures */
   2.142 +static jmethodID midPollInputDevices;
   2.143 +static jmethodID midPollHapticDevices;
   2.144 +static jmethodID midHapticRun;
   2.145  
   2.146  /* static fields */
   2.147  static jfieldID fidSeparateMouseAndTouch;
   2.148 @@ -245,7 +266,17 @@
   2.149      return JNI_VERSION_1_4;
   2.150  }
   2.151  
   2.152 -/* Called before SDL_main() to initialize JNI bindings */
   2.153 +void checkJNIReady()
   2.154 +{
   2.155 +    if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) {
   2.156 +        // We aren't fully initialized, let's just return.
   2.157 +        return;
   2.158 +    }
   2.159 +
   2.160 +    SDL_SetMainReady();    
   2.161 +}
   2.162 +
   2.163 +/* Activity initialization -- called before SDL_main() to initialize JNI bindings */
   2.164  JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
   2.165  {
   2.166      __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()");
   2.167 @@ -256,28 +287,6 @@
   2.168  
   2.169      midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.170                                  "getNativeSurface","()Landroid/view/Surface;");
   2.171 -    midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.172 -                                "audioOpen", "(IZZI)I");
   2.173 -    midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.174 -                                "audioWriteShortBuffer", "([S)V");
   2.175 -    midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.176 -                                "audioWriteByteBuffer", "([B)V");
   2.177 -    midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.178 -                                "audioClose", "()V");
   2.179 -    midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.180 -                                "captureOpen", "(IZZI)I");
   2.181 -    midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.182 -                                "captureReadShortBuffer", "([SZ)I");
   2.183 -    midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.184 -                                "captureReadByteBuffer", "([BZ)I");
   2.185 -    midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.186 -                                "captureClose", "()V");
   2.187 -    midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.188 -                                "pollInputDevices", "()V");
   2.189 -    midPollHapticDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.190 -                                "pollHapticDevices", "()V");
   2.191 -    midHapticRun = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.192 -                                "hapticRun", "(II)V");
   2.193      midSetActivityTitle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.194                                  "setActivityTitle","(Ljava/lang/String;)Z");
   2.195      midSetOrientation = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.196 @@ -298,16 +307,14 @@
   2.197                                  "clipboardGetText", "()Ljava/lang/String;");
   2.198      midClipboardHasText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.199                                  "clipboardHasText", "()Z");
   2.200 -
   2.201 -    bHasNewData = SDL_FALSE;
   2.202 +    midOpenAPKExpansionInputStream = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   2.203 +                                "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
   2.204  
   2.205      if (!midGetNativeSurface ||
   2.206 -       !midAudioOpen || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioClose ||
   2.207 -       !midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose ||
   2.208 -       !midPollInputDevices || !midPollHapticDevices || !midHapticRun ||
   2.209         !midSetActivityTitle || !midSetOrientation || !midGetContext || !midInputGetInputDeviceIds ||
   2.210         !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown || 
   2.211 -       !midClipboardSetText || !midClipboardGetText || !midClipboardHasText) {
   2.212 +       !midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
   2.213 +       !midOpenAPKExpansionInputStream) {
   2.214          __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
   2.215      }
   2.216  
   2.217 @@ -317,7 +324,64 @@
   2.218          __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java static fields, do you have the latest version of SDLActivity.java?");
   2.219      }
   2.220  
   2.221 -    SDL_SetMainReady();
   2.222 +    checkJNIReady();
   2.223 +}
   2.224 +
   2.225 +/* Audio initialization -- called before SDL_main() to initialize JNI bindings */
   2.226 +JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
   2.227 +{
   2.228 +    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()");
   2.229 +
   2.230 +    Android_JNI_SetupThread();
   2.231 +
   2.232 +    mAudioManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
   2.233 +
   2.234 +    midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   2.235 +                                "audioOpen", "(IZZI)I");
   2.236 +    midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   2.237 +                                "audioWriteShortBuffer", "([S)V");
   2.238 +    midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   2.239 +                                "audioWriteByteBuffer", "([B)V");
   2.240 +    midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   2.241 +                                "audioClose", "()V");
   2.242 +    midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   2.243 +                                "captureOpen", "(IZZI)I");
   2.244 +    midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   2.245 +                                "captureReadShortBuffer", "([SZ)I");
   2.246 +    midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   2.247 +                                "captureReadByteBuffer", "([BZ)I");
   2.248 +    midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   2.249 +                                "captureClose", "()V");
   2.250 +
   2.251 +    if (!midAudioOpen || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioClose ||
   2.252 +       !midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose) {
   2.253 +        __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
   2.254 +    }
   2.255 +
   2.256 +    checkJNIReady();
   2.257 +}
   2.258 +
   2.259 +/* Controller initialization -- called before SDL_main() to initialize JNI bindings */
   2.260 +JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
   2.261 +{
   2.262 +    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()");
   2.263 +
   2.264 +    Android_JNI_SetupThread();
   2.265 +
   2.266 +    mControllerManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
   2.267 +
   2.268 +    midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
   2.269 +                                "pollInputDevices", "()V");
   2.270 +    midPollHapticDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
   2.271 +                                "pollHapticDevices", "()V");
   2.272 +    midHapticRun = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
   2.273 +                                "hapticRun", "(II)V");
   2.274 +
   2.275 +    if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun) {
   2.276 +        __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
   2.277 +    }
   2.278 +
   2.279 +    checkJNIReady();
   2.280  }
   2.281  
   2.282  /* SDL main function prototype */
   2.283 @@ -421,7 +485,7 @@
   2.284  }
   2.285  
   2.286  /* Paddown */
   2.287 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadDown)(
   2.288 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
   2.289                                      JNIEnv* env, jclass jcls,
   2.290                                      jint device_id, jint keycode)
   2.291  {
   2.292 @@ -429,7 +493,7 @@
   2.293  }
   2.294  
   2.295  /* Padup */
   2.296 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(onNativePadUp)(
   2.297 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
   2.298                                      JNIEnv* env, jclass jcls,
   2.299                                      jint device_id, jint keycode)
   2.300  {
   2.301 @@ -437,7 +501,7 @@
   2.302  }
   2.303  
   2.304  /* Joy */
   2.305 -JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeJoy)(
   2.306 +JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
   2.307                                      JNIEnv* env, jclass jcls,
   2.308                                      jint device_id, jint axis, jfloat value)
   2.309  {
   2.310 @@ -445,7 +509,7 @@
   2.311  }
   2.312  
   2.313  /* POV Hat */
   2.314 -JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeHat)(
   2.315 +JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
   2.316                                      JNIEnv* env, jclass jcls,
   2.317                                      jint device_id, jint hat_id, jint x, jint y)
   2.318  {
   2.319 @@ -453,7 +517,7 @@
   2.320  }
   2.321  
   2.322  
   2.323 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddJoystick)(
   2.324 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
   2.325                                      JNIEnv* env, jclass jcls,
   2.326                                      jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
   2.327                                      jint nbuttons, jint naxes, jint nhats, jint nballs)
   2.328 @@ -470,14 +534,14 @@
   2.329      return retval;
   2.330  }
   2.331  
   2.332 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveJoystick)(
   2.333 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
   2.334                                      JNIEnv* env, jclass jcls,
   2.335                                      jint device_id)
   2.336  {
   2.337      return Android_RemoveJoystick(device_id);
   2.338  }
   2.339  
   2.340 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddHaptic)(
   2.341 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
   2.342      JNIEnv* env, jclass jcls, jint device_id, jstring device_name)
   2.343  {
   2.344      int retval;
   2.345 @@ -490,7 +554,7 @@
   2.346      return retval;
   2.347  }
   2.348  
   2.349 -JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveHaptic)(
   2.350 +JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
   2.351      JNIEnv* env, jclass jcls, jint device_id)
   2.352  {
   2.353      return Android_RemoveHaptic(device_id);
   2.354 @@ -882,7 +946,7 @@
   2.355      if (iscapture) {
   2.356          __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
   2.357          captureBuffer16Bit = is16Bit;
   2.358 -        if ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
   2.359 +        if ((*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
   2.360              /* Error during audio initialization */
   2.361              __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioRecord initialization!");
   2.362              return 0;
   2.363 @@ -890,7 +954,7 @@
   2.364      } else {
   2.365          __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
   2.366          audioBuffer16Bit = is16Bit;
   2.367 -        if ((*env)->CallStaticIntMethod(env, mActivityClass, midAudioOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
   2.368 +        if ((*env)->CallStaticIntMethod(env, mAudioManagerClass, midAudioOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
   2.369              /* Error during audio initialization */
   2.370              __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
   2.371              return 0;
   2.372 @@ -958,10 +1022,10 @@
   2.373  
   2.374      if (audioBuffer16Bit) {
   2.375          (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
   2.376 -        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
   2.377 +        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
   2.378      } else {
   2.379          (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
   2.380 -        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
   2.381 +        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
   2.382      }
   2.383  
   2.384      /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
   2.385 @@ -975,7 +1039,7 @@
   2.386  
   2.387      if (captureBuffer16Bit) {
   2.388          SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / 2));
   2.389 -        br = (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
   2.390 +        br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
   2.391          if (br > 0) {
   2.392              jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
   2.393              br *= 2;
   2.394 @@ -984,7 +1048,7 @@
   2.395          }
   2.396      } else {
   2.397          SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen);
   2.398 -        br = (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
   2.399 +        br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
   2.400          if (br > 0) {
   2.401              jbyte *ptr = (*env)->GetByteArrayElements(env, (jbyteArray)captureBuffer, &isCopy);
   2.402              SDL_memcpy(buffer, ptr, br);
   2.403 @@ -1008,9 +1072,9 @@
   2.404      }
   2.405  #else
   2.406      if (captureBuffer16Bit) {
   2.407 -        (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
   2.408 +        (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
   2.409      } else {
   2.410 -        (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
   2.411 +        (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
   2.412      }
   2.413  #endif
   2.414  }
   2.415 @@ -1020,13 +1084,13 @@
   2.416      JNIEnv *env = Android_JNI_GetEnv();
   2.417  
   2.418      if (iscapture) {
   2.419 -        (*env)->CallStaticVoidMethod(env, mActivityClass, midCaptureClose);
   2.420 +        (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midCaptureClose);
   2.421          if (captureBuffer) {
   2.422              (*env)->DeleteGlobalRef(env, captureBuffer);
   2.423              captureBuffer = NULL;
   2.424          }
   2.425      } else {
   2.426 -        (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioClose);
   2.427 +        (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioClose);
   2.428          if (audioBuffer) {
   2.429              (*env)->DeleteGlobalRef(env, audioBuffer);
   2.430              audioBuffer = NULL;
   2.431 @@ -1160,13 +1224,7 @@
   2.432          inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */);
   2.433          if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
   2.434              /* Try fallback to APK expansion files */
   2.435 -            mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
   2.436 -                "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
   2.437 -            if (!mid) {
   2.438 -                SDL_SetError("No openAPKExpansionInputStream() in Java class");
   2.439 -                goto failure; /* Java class is missing the required method */
   2.440 -            }
   2.441 -            inputStream = (*mEnv)->CallObjectMethod(mEnv, context, mid, fileNameJString);
   2.442 +            inputStream = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, midOpenAPKExpansionInputStream, fileNameJString);
   2.443  
   2.444              /* Exception is checked first because it always needs to be cleared.
   2.445               * If no exception occurred then the last SDL error message is kept.
   2.446 @@ -1673,19 +1731,19 @@
   2.447  void Android_JNI_PollInputDevices(void)
   2.448  {
   2.449      JNIEnv *env = Android_JNI_GetEnv();
   2.450 -    (*env)->CallStaticVoidMethod(env, mActivityClass, midPollInputDevices);
   2.451 +    (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices);
   2.452  }
   2.453  
   2.454  void Android_JNI_PollHapticDevices(void)
   2.455  {
   2.456      JNIEnv *env = Android_JNI_GetEnv();
   2.457 -    (*env)->CallStaticVoidMethod(env, mActivityClass, midPollHapticDevices);
   2.458 +    (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollHapticDevices);
   2.459  }
   2.460  
   2.461  void Android_JNI_HapticRun(int device_id, int length)
   2.462  {
   2.463      JNIEnv *env = Android_JNI_GetEnv();
   2.464 -    (*env)->CallStaticVoidMethod(env, mActivityClass, midHapticRun, device_id, length);
   2.465 +    (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRun, device_id, length);
   2.466  }
   2.467  
   2.468  
   2.469 @@ -1862,6 +1920,11 @@
   2.470  
   2.471          /* context = SDLActivity.getContext(); */
   2.472          context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
   2.473 +        if (!context) {
   2.474 +            SDL_SetError("Couldn't get Android context!");
   2.475 +            LocalReferenceHolder_Cleanup(&refs);
   2.476 +            return NULL;
   2.477 +        }
   2.478  
   2.479          /* fileObj = context.getFilesDir(); */
   2.480          mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),