From 42ff780b1f0470fc5b2cb5ee02e9d8e1150eaf67 Mon Sep 17 00:00:00 2001 From: Holger Schemel Date: Thu, 10 Feb 2022 20:02:12 +0100 Subject: [PATCH] updated SDL Java files for Android platform --- .../app/HIDDeviceBLESteamController.java | 6 +- .../java/org/libsdl/app/HIDDeviceManager.java | 76 +- .../java/org/libsdl/app/HIDDeviceUSB.java | 7 +- .../app/src/main/java/org/libsdl/app/SDL.java | 15 +- .../main/java/org/libsdl/app/SDLActivity.java | 662 +++++++++--------- .../java/org/libsdl/app/SDLAudioManager.java | 13 +- .../org/libsdl/app/SDLControllerManager.java | 178 ++--- 7 files changed, 491 insertions(+), 466 deletions(-) diff --git a/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java b/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java index 94a28189..65c5a423 100644 --- a/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java +++ b/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java @@ -564,10 +564,10 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe return "Steam Controller"; } - @Override + @Override public UsbDevice getDevice() { - return null; - } + return null; + } @Override public boolean open() { diff --git a/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java index 56f677e6..802c7254 100644 --- a/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java +++ b/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java @@ -7,6 +7,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; +import android.os.Build; import android.util.Log; import android.content.BroadcastReceiver; import android.content.Context; @@ -104,36 +105,6 @@ public class HIDDeviceManager { private HIDDeviceManager(final Context context) { mContext = context; - // Make sure we have the HIDAPI library loaded with the native functions - try { - SDL.loadLibrary("hidapi"); - } catch (Throwable e) { - Log.w(TAG, "Couldn't load hidapi: " + e.toString()); - - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setCancelable(false); - builder.setTitle("SDL HIDAPI Error"); - builder.setMessage("Please report the following error to the SDL maintainers: " + e.getMessage()); - builder.setNegativeButton("Quit", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - try { - // If our context is an activity, exit rather than crashing when we can't - // call our native functions. - Activity activity = (Activity)context; - - activity.finish(); - } - catch (ClassCastException cce) { - // Context wasn't an activity, there's nothing we can do. Give up and return. - } - } - }); - builder.show(); - - return; - } - HIDDeviceRegisterCallback(); mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE); @@ -148,9 +119,6 @@ public class HIDDeviceManager { { mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0); } - - initializeUSB(); - initializeBluetooth(); } public Context getContext() { @@ -173,6 +141,9 @@ public class HIDDeviceManager { private void initializeUSB() { mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE); + if (mUsbManager == null) { + return; + } /* // Logging @@ -275,6 +246,7 @@ public class HIDDeviceManager { 0x15e4, // Numark 0x162e, // Joytech 0x1689, // Razer Onza + 0x1949, // Lab126, Inc. 0x1bad, // Harmonix 0x24c6, // PowerA }; @@ -353,9 +325,18 @@ public class HIDDeviceManager { private void connectHIDDeviceUSB(UsbDevice usbDevice) { synchronized (this) { + int interface_mask = 0; for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) { UsbInterface usbInterface = usbDevice.getInterface(interface_index); if (isHIDDeviceInterface(usbDevice, usbInterface)) { + // Check to see if we've already added this interface + // This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive + int interface_id = usbInterface.getId(); + if ((interface_mask & (1 << interface_id)) != 0) { + continue; + } + interface_mask |= (1 << interface_id); + HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index); int id = device.getId(); mDevicesById.put(id, device); @@ -368,11 +349,17 @@ public class HIDDeviceManager { private void initializeBluetooth() { Log.d(TAG, "Initializing Bluetooth"); - if (mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) { + if (Build.VERSION.SDK_INT <= 30 && + mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) { Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH"); return; } + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18)) { + Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE"); + return; + } + // Find bonded bluetooth controllers and create SteamControllers for them mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager == null) { @@ -555,6 +542,18 @@ public class HIDDeviceManager { ////////// JNI interface functions ////////////////////////////////////////////////////////////////////////////////////////////////////// + public boolean initialize(boolean usb, boolean bluetooth) { + Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")"); + + if (usb) { + initializeUSB(); + } + if (bluetooth) { + initializeBluetooth(); + } + return true; + } + public boolean openDevice(int deviceID) { Log.v(TAG, "openDevice deviceID=" + deviceID); HIDDevice device = getDevice(deviceID); @@ -568,7 +567,14 @@ public class HIDDeviceManager { if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) { HIDDeviceOpenPending(deviceID); try { - mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), 0)); + final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31 + int flags; + if (Build.VERSION.SDK_INT >= 31) { + flags = FLAG_MUTABLE; + } else { + flags = 0; + } + mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags)); } catch (Exception e) { Log.v(TAG, "Couldn't request permission for USB device " + usbDevice); HIDDeviceOpenResult(deviceID, false); diff --git a/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java b/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java index 33816e34..d20fe80b 100644 --- a/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java +++ b/build-projects/android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java @@ -53,7 +53,12 @@ class HIDDeviceUSB implements HIDDevice { public String getSerialNumber() { String result = null; if (Build.VERSION.SDK_INT >= 21) { - result = mDevice.getSerialNumber(); + try { + result = mDevice.getSerialNumber(); + } + catch (SecurityException exception) { + //Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage()); + } } if (result == null) { result = ""; diff --git a/build-projects/android/app/src/main/java/org/libsdl/app/SDL.java b/build-projects/android/app/src/main/java/org/libsdl/app/SDL.java index fb7f7319..dafc0cb8 100644 --- a/build-projects/android/app/src/main/java/org/libsdl/app/SDL.java +++ b/build-projects/android/app/src/main/java/org/libsdl/app/SDL.java @@ -2,7 +2,8 @@ package org.libsdl.app; import android.content.Context; -import java.lang.reflect.*; +import java.lang.Class; +import java.lang.reflect.Method; /** SDL library initialization @@ -51,16 +52,16 @@ public class SDL { // To use ReLinker, just add it as a dependency. For more information, see // https://github.com/KeepSafe/ReLinker for ReLinker's repository. // - Class relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker"); - Class relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener"); - Class contextClass = mContext.getClassLoader().loadClass("android.content.Context"); - Class stringClass = mContext.getClassLoader().loadClass("java.lang.String"); + Class relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker"); + Class relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener"); + Class contextClass = mContext.getClassLoader().loadClass("android.content.Context"); + Class stringClass = mContext.getClassLoader().loadClass("java.lang.String"); // Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if // they've changed during updates. Method forceMethod = relinkClass.getDeclaredMethod("force"); Object relinkInstance = forceMethod.invoke(null); - Class relinkInstanceClass = relinkInstance.getClass(); + Class relinkInstanceClass = relinkInstance.getClass(); // Actually load the library! Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass); @@ -77,7 +78,7 @@ public class SDL { catch (final SecurityException se) { throw se; } - } + } } protected static Context mContext; diff --git a/build-projects/android/app/src/main/java/org/libsdl/app/SDLActivity.java b/build-projects/android/app/src/main/java/org/libsdl/app/SDLActivity.java index a61dd6db..9144949b 100644 --- a/build-projects/android/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/build-projects/android/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -1,35 +1,62 @@ package org.libsdl.app; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Hashtable; -import java.lang.reflect.Method; -import java.lang.Math; - -import android.app.*; -import android.content.*; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.UiModeManager; +import android.content.ClipboardManager; +import android.content.ClipData; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; import android.text.InputType; -import android.view.*; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.SparseArray; +import android.view.Display; +import android.view.Gravity; +import android.view.InputDevice; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import android.widget.RelativeLayout; import android.widget.Button; import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; -import android.os.*; -import android.util.DisplayMetrics; -import android.util.Log; -import android.util.SparseArray; -import android.graphics.*; -import android.graphics.drawable.Drawable; -import android.hardware.*; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.content.pm.ApplicationInfo; +import android.widget.Toast; + +import java.util.Hashtable; +import java.util.Locale; + /** SDL Activity @@ -41,7 +68,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24); // Cursor types - private static final int SDL_SYSTEM_CURSOR_NONE = -1; + // private static final int SDL_SYSTEM_CURSOR_NONE = -1; private static final int SDL_SYSTEM_CURSOR_ARROW = 0; private static final int SDL_SYSTEM_CURSOR_IBEAM = 1; private static final int SDL_SYSTEM_CURSOR_WAIT = 2; @@ -62,6 +89,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh protected static final int SDL_ORIENTATION_PORTRAIT_FLIPPED = 4; protected static int mCurrentOrientation; + protected static Locale mCurrentLocale; // Handle the state of the native layer public enum NativeState { @@ -72,7 +100,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static NativeState mCurrentNativeState; /** If shared libraries (e.g. SDL or the native application) could not be loaded. */ - public static boolean mBrokenLibraries; + public static boolean mBrokenLibraries = true; // Main components protected static SDLActivity mSingleton; @@ -93,8 +121,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh if (mMotionListener == null) { if (Build.VERSION.SDK_INT >= 26) { mMotionListener = new SDLGenericMotionListener_API26(); - } else - if (Build.VERSION.SDK_INT >= 24) { + } else if (Build.VERSION.SDK_INT >= 24) { mMotionListener = new SDLGenericMotionListener_API24(); } else { mMotionListener = new SDLGenericMotionListener_API12(); @@ -137,7 +164,6 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh */ protected String[] getLibraries() { return new String[] { - "hidapi", "SDL2", "SDL2_image", "SDL2_mixer", @@ -175,7 +201,6 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh mCursors = new Hashtable(); mLastCursorID = 0; mSDLThread = null; - mBrokenLibraries = false; mIsResumedCalled = false; mHasFocus = true; mNextNativeState = NativeState.INIT; @@ -200,6 +225,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh String errorMsgBrokenLib = ""; try { loadLibraries(); + mBrokenLibraries = false; /* success */ } catch(UnsatisfiedLinkError e) { System.err.println(e.getMessage()); mBrokenLibraries = true; @@ -243,7 +269,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh mSingleton = this; SDL.setContext(this); - mClipboardHandler = new SDLClipboardHandler_API11(); + mClipboardHandler = new SDLClipboardHandler(); mHIDDeviceManager = HIDDeviceManager.acquire(this); @@ -258,6 +284,15 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh // Only record current orientation SDLActivity.onNativeOrientationChanged(mCurrentOrientation); + try { + if (Build.VERSION.SDK_INT < 24) { + mCurrentLocale = getContext().getResources().getConfiguration().locale; + } else { + mCurrentLocale = getContext().getResources().getConfiguration().getLocales().get(0); + } + } catch(Exception ignored) { + } + setContentView(mLayout); setWindowStyle(false); @@ -343,11 +378,14 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh } public static int getCurrentOrientation() { - final Context context = SDLActivity.getContext(); - final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); - int result = SDL_ORIENTATION_UNKNOWN; + Activity activity = (Activity)getContext(); + if (activity == null) { + return result; + } + Display display = activity.getWindowManager().getDefaultDisplay(); + switch (display.getRotation()) { case Surface.ROTATION_0: result = SDL_ORIENTATION_PORTRAIT; @@ -407,6 +445,21 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh SDLActivity.nativeLowMemory(); } + @Override + public void onConfigurationChanged(Configuration newConfig) { + Log.v(TAG, "onConfigurationChanged()"); + super.onConfigurationChanged(newConfig); + + if (SDLActivity.mBrokenLibraries) { + return; + } + + if (mCurrentLocale == null || !mCurrentLocale.equals(newConfig.locale)) { + mCurrentLocale = newConfig.locale; + SDLActivity.onNativeLocaleChanged(); + } + } + @Override protected void onDestroy() { Log.v(TAG, "onDestroy()"); @@ -446,8 +499,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh // If we do, the normal hardware back button will no longer work and people have to use home, // but the mouse right click will work. // - String trapBack = SDLActivity.nativeGetHint("SDL_ANDROID_TRAP_BACK_BUTTON"); - if ((trapBack != null) && trapBack.equals("1")) { + boolean trapBack = SDLActivity.nativeGetHintBoolean("SDL_ANDROID_TRAP_BACK_BUTTON", false); + if (trapBack) { // Exit and let the mouse handler handle this button (if appropriate) return; } @@ -540,11 +593,10 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh mSDLThread.start(); // No nativeResume(), don't signal Android_ResumeSem - mSurface.handleResume(); } else { nativeResume(); - mSurface.handleResume(); } + mSurface.handleResume(); mCurrentNativeState = mNextNativeState; } @@ -555,7 +607,6 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh static final int COMMAND_CHANGE_TITLE = 1; static final int COMMAND_CHANGE_WINDOW_STYLE = 2; static final int COMMAND_TEXTEDIT_HIDE = 3; - static final int COMMAND_CHANGE_SURFACEVIEW_FORMAT = 4; static final int COMMAND_SET_KEEP_SCREEN_ON = 5; protected static final int COMMAND_USER = 0x8000; @@ -596,34 +647,32 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh } break; case COMMAND_CHANGE_WINDOW_STYLE: - if (Build.VERSION.SDK_INT < 19) { - // This version of Android doesn't support the immersive fullscreen mode - break; - } - if (context instanceof Activity) { - Window window = ((Activity) context).getWindow(); - if (window != null) { - if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { - int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | + if (Build.VERSION.SDK_INT >= 19) { + if (context instanceof Activity) { + Window window = ((Activity) context).getWindow(); + if (window != null) { + if ((msg.obj instanceof Integer) && ((Integer) msg.obj != 0)) { + int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE; - window.getDecorView().setSystemUiVisibility(flags); - window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - SDLActivity.mFullscreenModeActive = true; - } else { - int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE; - window.getDecorView().setSystemUiVisibility(flags); - window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - SDLActivity.mFullscreenModeActive = false; + window.getDecorView().setSystemUiVisibility(flags); + window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + SDLActivity.mFullscreenModeActive = true; + } else { + int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE; + window.getDecorView().setSystemUiVisibility(flags); + window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + SDLActivity.mFullscreenModeActive = false; + } } + } else { + Log.e(TAG, "error handling message, getContext() returned no Activity"); } - } else { - Log.e(TAG, "error handling message, getContext() returned no Activity"); } break; case COMMAND_TEXTEDIT_HIDE: @@ -646,7 +695,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh if (context instanceof Activity) { Window window = ((Activity) context).getWindow(); if (window != null) { - if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { + if ((msg.obj instanceof Integer) && ((Integer) msg.obj != 0)) { window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } else { window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); @@ -655,32 +704,6 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh } break; } - case COMMAND_CHANGE_SURFACEVIEW_FORMAT: - { - int format = (Integer) msg.obj; - int pf; - - if (SDLActivity.mSurface == null) { - return; - } - - SurfaceHolder holder = SDLActivity.mSurface.getHolder(); - if (holder == null) { - return; - } - - if (format == 1) { - pf = PixelFormat.RGBA_8888; - } else if (format == 2) { - pf = PixelFormat.RGBX_8888; - } else { - pf = PixelFormat.RGB_565; - } - - holder.setFormat(pf); - - break; - } default: if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { Log.e(TAG, "error handling message, command is " + msg.arg1); @@ -699,53 +722,53 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh msg.obj = data; boolean result = commandHandler.sendMessage(msg); - if ((Build.VERSION.SDK_INT >= 19) && (command == COMMAND_CHANGE_WINDOW_STYLE)) { - // Ensure we don't return until the resize has actually happened, - // or 500ms have passed. - - boolean bShouldWait = false; - - if (data instanceof Integer) { - // Let's figure out if we're already laid out fullscreen or not. - Display display = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); - android.util.DisplayMetrics realMetrics = new android.util.DisplayMetrics(); - display.getRealMetrics( realMetrics ); - - boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) && - (realMetrics.heightPixels == mSurface.getHeight())); - - if (((Integer)data).intValue() == 1) { - // If we aren't laid out fullscreen or actively in fullscreen mode already, we're going - // to change size and should wait for surfaceChanged() before we return, so the size - // is right back in native code. If we're already laid out fullscreen, though, we're - // not going to change size even if we change decor modes, so we shouldn't wait for - // surfaceChanged() -- which may not even happen -- and should return immediately. - bShouldWait = !bFullscreenLayout; - } - else { - // If we're laid out fullscreen (even if the status bar and nav bar are present), - // or are actively in fullscreen, we're going to change size and should wait for - // surfaceChanged before we return, so the size is right back in native code. - bShouldWait = bFullscreenLayout; + if (Build.VERSION.SDK_INT >= 19) { + if (command == COMMAND_CHANGE_WINDOW_STYLE) { + // Ensure we don't return until the resize has actually happened, + // or 500ms have passed. + + boolean bShouldWait = false; + + if (data instanceof Integer) { + // Let's figure out if we're already laid out fullscreen or not. + Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + DisplayMetrics realMetrics = new DisplayMetrics(); + display.getRealMetrics(realMetrics); + + boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) && + (realMetrics.heightPixels == mSurface.getHeight())); + + if ((Integer) data == 1) { + // If we aren't laid out fullscreen or actively in fullscreen mode already, we're going + // to change size and should wait for surfaceChanged() before we return, so the size + // is right back in native code. If we're already laid out fullscreen, though, we're + // not going to change size even if we change decor modes, so we shouldn't wait for + // surfaceChanged() -- which may not even happen -- and should return immediately. + bShouldWait = !bFullscreenLayout; + } else { + // If we're laid out fullscreen (even if the status bar and nav bar are present), + // or are actively in fullscreen, we're going to change size and should wait for + // surfaceChanged before we return, so the size is right back in native code. + bShouldWait = bFullscreenLayout; + } } - } - if (bShouldWait && (SDLActivity.getContext() != null)) { - // We'll wait for the surfaceChanged() method, which will notify us - // when called. That way, we know our current size is really the - // size we need, instead of grabbing a size that's still got - // the navigation and/or status bars before they're hidden. - // - // We'll wait for up to half a second, because some devices - // take a surprisingly long time for the surface resize, but - // then we'll just give up and return. - // - synchronized(SDLActivity.getContext()) { - try { - SDLActivity.getContext().wait(500); - } - catch (InterruptedException ie) { - ie.printStackTrace(); + if (bShouldWait && (SDLActivity.getContext() != null)) { + // We'll wait for the surfaceChanged() method, which will notify us + // when called. That way, we know our current size is really the + // size we need, instead of grabbing a size that's still got + // the navigation and/or status bars before they're hidden. + // + // We'll wait for up to half a second, because some devices + // take a surprisingly long time for the surface resize, but + // then we'll just give up and return. + // + synchronized (SDLActivity.getContext()) { + try { + SDLActivity.getContext().wait(500); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } } } } @@ -764,7 +787,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static native void nativeResume(); public static native void nativeFocusChanged(boolean hasFocus); public static native void onNativeDropFile(String filename); - public static native void nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, int format, float rate); + public static native void nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, float rate); public static native void onNativeResize(); public static native void onNativeKeyDown(int keycode); public static native void onNativeKeyUp(int keycode); @@ -780,10 +803,12 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static native void onNativeSurfaceChanged(); public static native void onNativeSurfaceDestroyed(); public static native String nativeGetHint(String name); + public static native boolean nativeGetHintBoolean(String name, boolean default_value); public static native void nativeSetenv(String name, String value); public static native void onNativeOrientationChanged(int orientation); public static native void nativeAddTouch(int touchId, String name); public static native void nativePermissionResult(int requestCode, boolean result); + public static native void onNativeLocaleChanged(); /** * This method is called by SDL using JNI. @@ -838,9 +863,9 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; } - boolean is_landscape_allowed = (orientation_landscape == -1 ? false : true); - boolean is_portrait_allowed = (orientation_portrait == -1 ? false : true); - int req = -1; /* Requested orientation */ + boolean is_landscape_allowed = (orientation_landscape != -1); + boolean is_portrait_allowed = (orientation_portrait != -1); + int req; /* Requested orientation */ /* No valid hint, nothing is explicitly allowed */ if (!is_portrait_allowed && !is_landscape_allowed) { @@ -872,7 +897,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh } } - Log.v("SDL", "setOrientation() requestedOrientation=" + req + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint); + Log.v(TAG, "setOrientation() requestedOrientation=" + req + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint); mSingleton.setRequestedOrientation(req); } @@ -938,11 +963,6 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh */ public static boolean supportsRelativeMouse() { - // ChromeOS doesn't provide relative mouse motion via the Android 7 APIs - if (isChromebook()) { - return false; - } - // DeX mode in Samsung Experience 9.0 and earlier doesn't support relative mice properly under // Android 7 APIs, and simply returns no data under Android 8 APIs. // @@ -976,7 +996,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh if (mSingleton == null) { return false; } - return mSingleton.sendCommand(command, Integer.valueOf(param)); + return mSingleton.sendCommand(command, param); } /** @@ -1000,30 +1020,30 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh if (Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.equals("X96-W")) { return true; } - if (Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.startsWith("TV")) { - return true; - } - return false; + return Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.startsWith("TV"); } - /** - * This method is called by SDL using JNI. - */ - public static boolean isTablet() { + public static double getDiagonal() + { DisplayMetrics metrics = new DisplayMetrics(); Activity activity = (Activity)getContext(); if (activity == null) { - return false; + return 0.0; } activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); double dWidthInches = metrics.widthPixels / (double)metrics.xdpi; double dHeightInches = metrics.heightPixels / (double)metrics.ydpi; - double dDiagonal = Math.sqrt((dWidthInches * dWidthInches) + (dHeightInches * dHeightInches)); + return Math.sqrt((dWidthInches * dWidthInches) + (dHeightInches * dHeightInches)); + } + /** + * This method is called by SDL using JNI. + */ + public static boolean isTablet() { // If our diagonal size is seven inches or greater, we consider ourselves a tablet. - return (dDiagonal >= 7.0); + return (getDiagonal() >= 7.0); } /** @@ -1045,7 +1065,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh } try { final Configuration config = getContext().getResources().getConfiguration(); - final Class configClass = config.getClass(); + final Class configClass = config.getClass(); return configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass) == configClass.getField("semDesktopModeEnabled").getInt(config); } catch(Exception ignored) { @@ -1065,6 +1085,10 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh */ public static boolean getManifestEnvironmentVariables() { try { + if (getContext() == null) { + return false; + } + ApplicationInfo applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA); Bundle bundle = applicationInfo.metaData; if (bundle == null) { @@ -1082,7 +1106,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh /* environment variables set! */ return true; } catch (Exception e) { - Log.v("SDL", "exception " + e.toString()); + Log.v(TAG, "exception " + e.toString()); } return false; } @@ -1090,7 +1114,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh // This method is called by SDLControllerManager's API 26 Generic Motion Handler. public static View getContentView() { - return mSingleton.mLayout; + return mLayout; } static class ShowTextInputTask implements Runnable { @@ -1170,14 +1194,6 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh return SDLActivity.mSurface.getNativeSurface(); } - /** - * This method is called by SDL using JNI. - */ - public static void setSurfaceViewFormat(int format) { - mSingleton.sendCommand(COMMAND_CHANGE_SURFACEVIEW_FORMAT, format); - return; - } - // Input /** @@ -1186,92 +1202,19 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static void initTouch() { int[] ids = InputDevice.getDeviceIds(); - for (int i = 0; i < ids.length; ++i) { - InputDevice device = InputDevice.getDevice(ids[i]); + for (int id : ids) { + InputDevice device = InputDevice.getDevice(id); if (device != null && (device.getSources() & InputDevice.SOURCE_TOUCHSCREEN) != 0) { nativeAddTouch(device.getId(), device.getName()); } } } - // APK expansion files support - - /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */ - private static Object expansionFile; - - /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */ - 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 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"); - if (mainHint == null) { - return null; // no expansion use if no main version was set - } - String patchHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION"); - if (patchHint == null) { - return null; // no expansion use if no patch version was set - } - - Integer mainVersion; - Integer patchVersion; - try { - mainVersion = Integer.valueOf(mainHint); - patchVersion = Integer.valueOf(patchHint); - } catch (NumberFormatException ex) { - ex.printStackTrace(); - throw new IOException("No valid file versions set for APK expansion files", ex); - } - - try { - // To avoid direct dependency on Google APK expansion library that is - // 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, SDL.getContext(), mainVersion, patchVersion); - - expansionFileMethod = expansionFile.getClass() - .getMethod("getInputStream", String.class); - } catch (Exception ex) { - ex.printStackTrace(); - expansionFile = null; - expansionFileMethod = null; - throw new IOException("Could not access APK expansion support library", ex); - } - } - - // Get an input stream for a known file inside the expansion file ZIPs - InputStream fileStream; - try { - fileStream = (InputStream)expansionFileMethod.invoke(expansionFile, fileName); - } catch (Exception ex) { - // calling "getInputStream" failed - ex.printStackTrace(); - throw new IOException("Could not open stream from APK expansion file", ex); - } - - if (fileStream == null) { - // calling "getInputStream" was successful but null was returned - throw new IOException("Could not find path in APK expansion file"); - } - - return fileStream; - } - // Messagebox /** Result of current messagebox. Also used for blocking the calling thread. */ protected final int[] messageboxSelection = new int[1]; - /** Id of current dialog. */ - protected int dialogs = 0; - /** * This method is called by SDL using JNI. * Shows the messagebox from UI thread and block calling thread. @@ -1315,7 +1258,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh runOnUiThread(new Runnable() { @Override public void run() { - showDialog(dialogs++, args); + messageboxCreateAndShow(args); } }); @@ -1335,8 +1278,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh return messageboxSelection[0]; } - @Override - protected Dialog onCreateDialog(int ignore, Bundle args) { + protected void messageboxCreateAndShow(Bundle args) { // TODO set values from "flags" to messagebox dialog @@ -1365,7 +1307,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh // create dialog with title and a listener to wake up calling thread - final Dialog dialog = new Dialog(this); + final AlertDialog dialog = new AlertDialog.Builder(this).create(); dialog.setTitle(args.getString("title")); dialog.setCancelable(false); dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @@ -1451,7 +1393,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh // add content to dialog and return - dialog.setContentView(content); + dialog.setView(content); dialog.setOnKeyListener(new Dialog.OnKeyListener() { @Override public boolean onKey(DialogInterface d, int keyCode, KeyEvent event) { @@ -1466,20 +1408,22 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh } }); - return dialog; + dialog.show(); } private final Runnable rehideSystemUi = new Runnable() { @Override public void run() { - int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | + if (Build.VERSION.SDK_INT >= 19) { + int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE; - SDLActivity.this.getWindow().getDecorView().setSystemUiVisibility(flags); + SDLActivity.this.getWindow().getDecorView().setSystemUiVisibility(flags); + } } }; @@ -1535,6 +1479,19 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh return mLastCursorID; } + /** + * This method is called by SDL using JNI. + */ + public static void destroyCustomCursor(int cursorID) { + if (Build.VERSION.SDK_INT >= 24) { + try { + mCursors.remove(cursorID); + } catch (Exception e) { + } + } + return; + } + /** * This method is called by SDL using JNI. */ @@ -1624,11 +1581,78 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - nativePermissionResult(requestCode, true); - } else { - nativePermissionResult(requestCode, false); + boolean result = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED); + nativePermissionResult(requestCode, result); + } + + /** + * This method is called by SDL using JNI. + */ + public static int openURL(String url) + { + try { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + + int flags = Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_MULTIPLE_TASK; + if (Build.VERSION.SDK_INT >= 21) { + flags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT; + } else { + flags |= Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET; + } + i.addFlags(flags); + + mSingleton.startActivity(i); + } catch (Exception ex) { + return -1; + } + return 0; + } + + /** + * This method is called by SDL using JNI. + */ + public static int showToast(String message, int duration, int gravity, int xOffset, int yOffset) + { + if(null == mSingleton) { + return - 1; + } + + try + { + class OneShotTask implements Runnable { + String mMessage; + int mDuration; + int mGravity; + int mXOffset; + int mYOffset; + + OneShotTask(String message, int duration, int gravity, int xOffset, int yOffset) { + mMessage = message; + mDuration = duration; + mGravity = gravity; + mXOffset = xOffset; + mYOffset = yOffset; + } + + public void run() { + try + { + Toast toast = Toast.makeText(mSingleton, mMessage, mDuration); + if (mGravity >= 0) { + toast.setGravity(mGravity, mXOffset, mYOffset); + } + toast.show(); + } catch(Exception ex) { + Log.e(TAG, ex.getMessage()); + } + } + } + mSingleton.runOnUiThread(new OneShotTask(message, duration, gravity, xOffset, yOffset)); + } catch(Exception ex) { + return -1; } + return 0; } } @@ -1655,13 +1679,12 @@ class SDLMain implements Runnable { Log.v("SDL", "Finished main function"); - if (SDLActivity.mSingleton == null || SDLActivity.mSingleton.isFinishing()) { - // Activity is already being destroyed - } else { + if (SDLActivity.mSingleton != null && !SDLActivity.mSingleton.isFinishing()) { // Let's finish the Activity SDLActivity.mSDLThread = null; SDLActivity.mSingleton.finish(); - } + } // else: Activity is already being destroyed + } } @@ -1755,30 +1778,6 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, return; } - int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default - switch (format) { - case PixelFormat.RGBA_8888: - Log.v("SDL", "pixel format RGBA_8888"); - sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888 - break; - case PixelFormat.RGBX_8888: - Log.v("SDL", "pixel format RGBX_8888"); - sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888 - break; - case PixelFormat.RGB_565: - Log.v("SDL", "pixel format RGB_565"); - sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 - break; - case PixelFormat.RGB_888: - Log.v("SDL", "pixel format RGB_888"); - // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead? - sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888 - break; - default: - Log.v("SDL", "pixel format unknown " + format); - break; - } - mWidth = width; mHeight = height; int nDeviceWidth = width; @@ -1786,13 +1785,13 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, try { if (Build.VERSION.SDK_INT >= 17) { - android.util.DisplayMetrics realMetrics = new android.util.DisplayMetrics(); + DisplayMetrics realMetrics = new DisplayMetrics(); mDisplay.getRealMetrics( realMetrics ); nDeviceWidth = realMetrics.widthPixels; nDeviceHeight = realMetrics.heightPixels; } + } catch(Exception ignored) { } - catch ( java.lang.Throwable throwable ) {} synchronized(SDLActivity.getContext()) { // In case we're waiting on a size change after going fullscreen, send a notification. @@ -1801,7 +1800,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, Log.v("SDL", "Window size: " + width + "x" + height); Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight); - SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, sdlFormat, mDisplay.getRefreshRate()); + SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate()); SDLActivity.onNativeResize(); // Prevent a screen distortion glitch, @@ -1809,12 +1808,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, boolean skip = false; int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation(); - if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) - { - // Accept any - } - else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) - { + if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) { if (mWidth > mHeight) { skip = true; } @@ -1868,6 +1862,19 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, int deviceId = event.getDeviceId(); int source = event.getSource(); + if (source == InputDevice.SOURCE_UNKNOWN) { + InputDevice device = InputDevice.getDevice(deviceId); + if (device != null) { + source = device.getSources(); + } + } + +// if (event.getAction() == KeyEvent.ACTION_DOWN) { +// Log.v("SDL", "key down: " + keyCode + ", deviceId = " + deviceId + ", source = " + source); +// } else if (event.getAction() == KeyEvent.ACTION_UP) { +// Log.v("SDL", "key up: " + keyCode + ", deviceId = " + deviceId + ", source = " + source); +// } + // Dispatch the different events depending on where they come from // Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD // So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD @@ -1888,24 +1895,14 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, } } - if (source == InputDevice.SOURCE_UNKNOWN) { - InputDevice device = InputDevice.getDevice(deviceId); - if (device != null) { - source = device.getSources(); - } - } - if ((source & InputDevice.SOURCE_KEYBOARD) != 0) { if (event.getAction() == KeyEvent.ACTION_DOWN) { - //Log.v("SDL", "key down: " + keyCode); if (SDLActivity.isTextInputEvent(event)) { SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1); } SDLActivity.onNativeKeyDown(keyCode); return true; - } - else if (event.getAction() == KeyEvent.ACTION_UP) { - //Log.v("SDL", "key up: " + keyCode); + } else if (event.getAction() == KeyEvent.ACTION_UP) { SDLActivity.onNativeKeyUp(keyCode); return true; } @@ -1932,22 +1929,34 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, @Override public boolean onTouch(View v, MotionEvent event) { /* Ref: http://developer.android.com/training/gestures/multi.html */ - final int touchDevId = event.getDeviceId(); + int touchDevId = event.getDeviceId(); final int pointerCount = event.getPointerCount(); int action = event.getActionMasked(); int pointerFingerId; - int mouseButton; int i = -1; float x,y,p; + /* + * Prevent id to be -1, since it's used in SDL internal for synthetic events + * Appears when using Android emulator, eg: + * adb shell input mouse tap 100 100 + * adb shell input touchscreen tap 100 100 + */ + if (touchDevId < 0) { + touchDevId -= 1; + } + // 12290 = Samsung DeX mode desktop mouse // 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN // 0x2 = SOURCE_CLASS_POINTER if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) { + int mouseButton = 1; try { - mouseButton = (Integer) event.getClass().getMethod("getButtonState").invoke(event); - } catch(Exception e) { - mouseButton = 1; // oh well. + Object object = event.getClass().getMethod("getButtonState").invoke(event); + if (object != null) { + mouseButton = (Integer) object; + } + } catch(Exception ignored) { } // We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values @@ -1978,6 +1987,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, case MotionEvent.ACTION_DOWN: // Primary pointer up/down, the index is always zero i = 0; + /* fallthrough */ case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_DOWN: // Non primary pointer up/down @@ -2044,7 +2054,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, // Since we may have an orientation set, we won't receive onConfigurationChanged events. // We thus should check here. - int newOrientation = SDLActivity.SDL_ORIENTATION_UNKNOWN; + int newOrientation; float x, y; switch (mDisplay.getRotation()) { @@ -2063,6 +2073,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, y = -event.values[1]; newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED; break; + case Surface.ROTATION_0: default: x = event.values[0]; y = event.values[1]; @@ -2109,8 +2120,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, // Change our action value to what SDL's code expects. if (action == MotionEvent.ACTION_BUTTON_PRESS) { action = MotionEvent.ACTION_DOWN; - } - else if (action == MotionEvent.ACTION_BUTTON_RELEASE) { + } else { /* MotionEvent.ACTION_BUTTON_RELEASE */ action = MotionEvent.ACTION_UP; } @@ -2275,45 +2285,38 @@ class SDLInputConnection extends BaseInputConnection { } } -interface SDLClipboardHandler { - - public boolean clipboardHasText(); - public String clipboardGetText(); - public void clipboardSetText(String string); - -} - - -class SDLClipboardHandler_API11 implements - SDLClipboardHandler, - android.content.ClipboardManager.OnPrimaryClipChangedListener { +class SDLClipboardHandler implements + ClipboardManager.OnPrimaryClipChangedListener { - protected android.content.ClipboardManager mClipMgr; + protected ClipboardManager mClipMgr; - SDLClipboardHandler_API11() { - mClipMgr = (android.content.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE); + SDLClipboardHandler() { + mClipMgr = (ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE); mClipMgr.addPrimaryClipChangedListener(this); } - @Override public boolean clipboardHasText() { - return mClipMgr.hasText(); + return mClipMgr.hasPrimaryClip(); } - @Override public String clipboardGetText() { - CharSequence text; - text = mClipMgr.getText(); - if (text != null) { - return text.toString(); + ClipData clip = mClipMgr.getPrimaryClip(); + if (clip != null) { + ClipData.Item item = clip.getItemAt(0); + if (item != null) { + CharSequence text = item.getText(); + if (text != null) { + return text.toString(); + } + } } return null; } - @Override public void clipboardSetText(String string) { mClipMgr.removePrimaryClipChangedListener(this); - mClipMgr.setText(string); + ClipData clip = ClipData.newPlainText(null, string); + mClipMgr.setPrimaryClip(clip); mClipMgr.addPrimaryClipChangedListener(this); } @@ -2321,6 +2324,5 @@ class SDLClipboardHandler_API11 implements public void onPrimaryClipChanged() { SDLActivity.onNativeClipboardChanged(); } - } diff --git a/build-projects/android/app/src/main/java/org/libsdl/app/SDLAudioManager.java b/build-projects/android/app/src/main/java/org/libsdl/app/SDLAudioManager.java index 0714419c..2bfc7186 100644 --- a/build-projects/android/app/src/main/java/org/libsdl/app/SDLAudioManager.java +++ b/build-projects/android/app/src/main/java/org/libsdl/app/SDLAudioManager.java @@ -1,6 +1,10 @@ package org.libsdl.app; -import android.media.*; +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioRecord; +import android.media.AudioTrack; +import android.media.MediaRecorder; import android.os.Build; import android.util.Log; @@ -43,6 +47,10 @@ public class SDLAudioManager if (desiredChannels > 2) { desiredChannels = 2; } + } + + /* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */ + if (Build.VERSION.SDK_INT < 22) { if (sampleRate < 8000) { sampleRate = 8000; } else if (sampleRate > 48000) { @@ -199,7 +207,6 @@ public class SDLAudioManager results[0] = mAudioRecord.getSampleRate(); results[1] = mAudioRecord.getAudioFormat(); results[2] = mAudioRecord.getChannelCount(); - results[3] = desiredFrames; } else { if (mAudioTrack == null) { @@ -223,8 +230,8 @@ public class SDLAudioManager results[0] = mAudioTrack.getSampleRate(); results[1] = mAudioTrack.getAudioFormat(); results[2] = mAudioTrack.getChannelCount(); - results[3] = desiredFrames; } + results[3] = desiredFrames; Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz"); diff --git a/build-projects/android/app/src/main/java/org/libsdl/app/SDLControllerManager.java b/build-projects/android/app/src/main/java/org/libsdl/app/SDLControllerManager.java index a81e97be..05e0f0ca 100644 --- a/build-projects/android/app/src/main/java/org/libsdl/app/SDLControllerManager.java +++ b/build-projects/android/app/src/main/java/org/libsdl/app/SDLControllerManager.java @@ -6,9 +6,14 @@ import java.util.Comparator; import java.util.List; import android.content.Context; -import android.os.*; -import android.view.*; +import android.os.Build; +import android.os.VibrationEffect; +import android.os.Vibrator; import android.util.Log; +import android.view.InputDevice; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; public class SDLControllerManager @@ -98,7 +103,7 @@ public class SDLControllerManager int sources = device.getSources(); /* This is called for every button press, so let's not spam the logs */ - /** + /* if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { Log.v(TAG, "Input device " + device.getName() + " has class joystick."); } @@ -108,7 +113,7 @@ public class SDLControllerManager if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) { Log.v(TAG, "Input device " + device.getName() + " is a gamepad."); } - **/ + */ return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 || ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) || @@ -167,7 +172,7 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler { } } - private ArrayList mJoysticks; + private final ArrayList mJoysticks; public SDLJoystickHandler_API16() { @@ -177,13 +182,14 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler { @Override public void pollInputDevices() { int[] deviceIds = InputDevice.getDeviceIds(); - for(int i=0; i < deviceIds.length; ++i) { - SDLJoystick joystick = getJoystick(deviceIds[i]); - if (joystick == null) { - joystick = new SDLJoystick(); - InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]); - if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) { - joystick.device_id = deviceIds[i]; + + for (int device_id : deviceIds) { + if (SDLControllerManager.isDeviceSDLJoystick(device_id)) { + SDLJoystick joystick = getJoystick(device_id); + if (joystick == null) { + InputDevice joystickDevice = InputDevice.getDevice(device_id); + joystick = new SDLJoystick(); + joystick.device_id = device_id; joystick.name = joystickDevice.getName(); joystick.desc = getJoystickDescriptor(joystickDevice); joystick.axes = new ArrayList(); @@ -191,53 +197,57 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler { List ranges = joystickDevice.getMotionRanges(); Collections.sort(ranges, new RangeComparator()); - for (InputDevice.MotionRange range : ranges ) { + 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) { + if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { joystick.hats.add(range); - } - else { + } else { joystick.axes.add(range); } } } mJoysticks.add(joystick); - SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, getVendorId(joystickDevice), getProductId(joystickDevice), false, getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0); + SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, + getVendorId(joystickDevice), getProductId(joystickDevice), false, + getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0); } } } /* Check removed devices */ - ArrayList removedDevices = new ArrayList(); - 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; + ArrayList removedDevices = null; + for (SDLJoystick joystick : mJoysticks) { + int device_id = joystick.device_id; + int i; + for (i = 0; i < deviceIds.length; i++) { + if (device_id == deviceIds[i]) break; } - if (j == deviceIds.length) { - removedDevices.add(Integer.valueOf(device_id)); + if (i == deviceIds.length) { + if (removedDevices == null) { + removedDevices = new ArrayList(); + } + removedDevices.add(device_id); } } - for(int i=0; i < removedDevices.size(); i++) { - int device_id = removedDevices.get(i).intValue(); - SDLControllerManager.nativeRemoveJoystick(device_id); - for (int j=0; j < mJoysticks.size(); j++) { - if (mJoysticks.get(j).device_id == device_id) { - mJoysticks.remove(j); - break; + if (removedDevices != null) { + for (int device_id : removedDevices) { + SDLControllerManager.nativeRemoveJoystick(device_id); + for (int i = 0; i < mJoysticks.size(); i++) { + if (mJoysticks.get(i).device_id == device_id) { + mJoysticks.remove(i); + 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); + for (SDLJoystick joystick : mJoysticks) { + if (joystick.device_id == device_id) { + return joystick; } } return null; @@ -248,25 +258,21 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler { 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; - SDLControllerManager.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 ) ); - SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY ); - } + if (action == 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; + SDLControllerManager.onNativeJoy(joystick.device_id, i, value); + } + for (int i = 0; i < joystick.hats.size() / 2; i++) { + int hatX = Math.round(event.getAxisValue(joystick.hats.get(2 * i).getAxis(), actionPointerIndex)); + int hatY = Math.round(event.getAxisValue(joystick.hats.get(2 * i + 1).getAxis(), actionPointerIndex)); + SDLControllerManager.onNativeHat(joystick.device_id, i, hatX, hatY); } - break; - default: - break; + } } } return true; @@ -432,13 +438,13 @@ class SDLHapticHandler_API26 extends SDLHapticHandler { class SDLHapticHandler { - class SDLHaptic { + static class SDLHaptic { public int device_id; public String name; public Vibrator vib; } - private ArrayList mHaptics; + private final ArrayList mHaptics; public SDLHapticHandler() { mHaptics = new ArrayList(); @@ -504,37 +510,41 @@ class SDLHapticHandler { } /* Check removed devices */ - ArrayList removedDevices = new ArrayList(); - 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; + ArrayList removedDevices = null; + for (SDLHaptic haptic : mHaptics) { + int device_id = haptic.device_id; + int i; + for (i = 0; i < deviceIds.length; i++) { + if (device_id == deviceIds[i]) 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); - } + if (device_id != deviceId_VIBRATOR_SERVICE || !hasVibratorService) { + if (i == deviceIds.length) { + if (removedDevices == null) { + removedDevices = new ArrayList(); + } + removedDevices.add(device_id); + } + } // else: don't remove the vibrator if it is still present } - for(int i=0; i < removedDevices.size(); i++) { - int device_id = removedDevices.get(i); - SDLControllerManager.nativeRemoveHaptic(device_id); - for (int j=0; j < mHaptics.size(); j++) { - if (mHaptics.get(j).device_id == device_id) { - mHaptics.remove(j); - break; + if (removedDevices != null) { + for (int device_id : removedDevices) { + SDLControllerManager.nativeRemoveHaptic(device_id); + for (int i = 0; i < mHaptics.size(); i++) { + if (mHaptics.get(i).device_id == device_id) { + mHaptics.remove(i); + 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); + for (SDLHaptic haptic : mHaptics) { + if (haptic.device_id == device_id) { + return haptic; } } return null; @@ -655,8 +665,7 @@ class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 { public float getEventX(MotionEvent event) { if (mRelativeModeEnabled) { return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X); - } - else { + } else { return event.getX(0); } } @@ -665,14 +674,12 @@ class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 { public float getEventY(MotionEvent event) { if (mRelativeModeEnabled) { return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y); - } - else { + } else { return event.getY(0); } } } - class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 { // Generic Motion (mouse hover, joystick...) events go here private boolean mRelativeModeEnabled; @@ -753,15 +760,12 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 { if (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)) { if (enabled) { SDLActivity.getContentView().requestPointerCapture(); - } - else { + } else { SDLActivity.getContentView().releasePointerCapture(); } mRelativeModeEnabled = enabled; return true; - } - else - { + } else { return false; } } -- 2.34.1