M src/android/app/build.gradle => src/android/app/build.gradle +7 -6
@@ 1,13 1,13 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 32
defaultConfig {
applicationId "org.rabbits.uxn"
minSdkVersion 16
- targetSdkVersion 32
- versionCode 7
- versionName "1.5"
+ compileSdkVersion 33
+ targetSdkVersion 33
+ versionCode 8
+ versionName "1.6"
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
@@ 32,10 32,11 @@ android {
path 'jni/Android.mk'
}
}
- lintOptions {
+ namespace 'org.rabbits.uxn'
+ lint {
abortOnError false
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
-}
+}<
\ No newline at end of file
M src/android/app/jni/SDL/SDL => src/android/app/jni/SDL/SDL +1 -1
@@ 1,1 1,1 @@
-Subproject commit 565ef63873c2dfa85e1e80db9dfbefbd9a8c67db
+Subproject commit 9b061c04e71be9e6729c06c3e64be95cf09244a2
M src/android/app/src/main/AndroidManifest.xml => src/android/app/src/main/AndroidManifest.xml +13 -9
@@ 3,7 3,7 @@
com.gamemaker.game
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.rabbits.uxn"
+ xmlns:tools="http://schemas.android.com/tools"
android:versionCode="2"
android:versionName="1.1"
android:installLocation="auto">
@@ 39,10 39,13 @@
android:required="false" /> -->
<uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
+ android:maxSdkVersion="32" />
<!-- Allow downloading to the external storage on Android 5.1 and older -->
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="22" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+ android:maxSdkVersion="32"
+ tools:ignore="ScopedStorage" />
<!-- Allow access to Bluetooth devices -->
<!-- Currently this is just for Steam Controller support and requires setting SDL_HINT_JOYSTICK_HIDAPI_STEAM -->
@@ 63,25 66,26 @@
An example Java class can be found in README-android.md
-->
- <application android:label="@string/app_name"
- android:icon="@mipmap/ic_launcher"
+ <application
android:allowBackup="true"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- android:hardwareAccelerated="true" >
+ android:hardwareAccelerated="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:enableOnBackInvokedCallback="true"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
<!-- Example of setting SDL hints from AndroidManifest.xml:
<meta-data android:name="SDL_ENV.SDL_ACCELEROMETER_AS_JOYSTICK" android:value="0"/>
-->
<activity android:name="Uxn"
- android:label="@string/app_name"
android:alwaysRetainTaskState="true"
android:launchMode="singleInstance"
android:configChanges="layoutDirection|locale|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
android:preferMinimalPostProcessing="true"
android:screenOrientation="fullSensor"
android:exported="true"
- >
+ tools:targetApi="r">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
M src/android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java => src/android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java +4 -0
@@ 248,7 248,9 @@ public class HIDDeviceManager {
0x1689, // Razer Onza
0x1949, // Lab126, Inc.
0x1bad, // Harmonix
+ 0x20d6, // PowerA
0x24c6, // PowerA
+ 0x2c22, // Qanba
};
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
@@ 274,7 276,9 @@ public class HIDDeviceManager {
0x0e6f, // PDP
0x0f0d, // Hori
0x1532, // Razer Wildcat
+ 0x20d6, // PowerA
0x24c6, // PowerA
+ 0x2dc8, /* 8BitDo */
0x2e24, // Hyperkin
};
M src/android/app/src/main/java/org/libsdl/app/SDLActivity.java => src/android/app/src/main/java/org/libsdl/app/SDLActivity.java +325 -544
@@ 17,20 17,18 @@ import android.content.res.Configuration;
import android.database.Cursor;
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.provider.OpenableColumns;
+import android.text.Editable;
import android.text.InputType;
+import android.text.Selection;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
@@ 38,11 36,8 @@ 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;
@@ 52,6 47,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
+import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ 68,7 64,116 @@ import java.util.Locale;
SDL Activity
*/
public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
- public static final String TAG = "Uxn";
+ private static final String TAG = "SDL";
+ private static final int SDL_MAJOR_VERSION = 2;
+ private static final int SDL_MINOR_VERSION = 26;
+ private static final int SDL_MICRO_VERSION = 3;
+/*
+ // Display InputType.SOURCE/CLASS of events and devices
+ //
+ // SDLActivity.debugSource(device.getSources(), "device[" + device.getName() + "]");
+ // SDLActivity.debugSource(event.getSource(), "event");
+ public static void debugSource(int sources, String prefix) {
+ int s = sources;
+ int s_copy = sources;
+ String cls = "";
+ String src = "";
+ int tst = 0;
+ int FLAG_TAINTED = 0x80000000;
+
+ if ((s & InputDevice.SOURCE_CLASS_BUTTON) != 0) cls += " BUTTON";
+ if ((s & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) cls += " JOYSTICK";
+ if ((s & InputDevice.SOURCE_CLASS_POINTER) != 0) cls += " POINTER";
+ if ((s & InputDevice.SOURCE_CLASS_POSITION) != 0) cls += " POSITION";
+ if ((s & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) cls += " TRACKBALL";
+
+
+ int s2 = s_copy & ~InputDevice.SOURCE_ANY; // keep class bits
+ s2 &= ~( InputDevice.SOURCE_CLASS_BUTTON
+ | InputDevice.SOURCE_CLASS_JOYSTICK
+ | InputDevice.SOURCE_CLASS_POINTER
+ | InputDevice.SOURCE_CLASS_POSITION
+ | InputDevice.SOURCE_CLASS_TRACKBALL);
+
+ if (s2 != 0) cls += "Some_Unkown";
+
+ s2 = s_copy & InputDevice.SOURCE_ANY; // keep source only, no class;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ tst = InputDevice.SOURCE_BLUETOOTH_STYLUS;
+ if ((s & tst) == tst) src += " BLUETOOTH_STYLUS";
+ s2 &= ~tst;
+ }
+
+ tst = InputDevice.SOURCE_DPAD;
+ if ((s & tst) == tst) src += " DPAD";
+ s2 &= ~tst;
+
+ tst = InputDevice.SOURCE_GAMEPAD;
+ if ((s & tst) == tst) src += " GAMEPAD";
+ s2 &= ~tst;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ tst = InputDevice.SOURCE_HDMI;
+ if ((s & tst) == tst) src += " HDMI";
+ s2 &= ~tst;
+ }
+
+ tst = InputDevice.SOURCE_JOYSTICK;
+ if ((s & tst) == tst) src += " JOYSTICK";
+ s2 &= ~tst;
+
+ tst = InputDevice.SOURCE_KEYBOARD;
+ if ((s & tst) == tst) src += " KEYBOARD";
+ s2 &= ~tst;
+
+ tst = InputDevice.SOURCE_MOUSE;
+ if ((s & tst) == tst) src += " MOUSE";
+ s2 &= ~tst;
+
+ if (Build.VERSION.SDK_INT >= 26) {
+ tst = InputDevice.SOURCE_MOUSE_RELATIVE;
+ if ((s & tst) == tst) src += " MOUSE_RELATIVE";
+ s2 &= ~tst;
+
+ tst = InputDevice.SOURCE_ROTARY_ENCODER;
+ if ((s & tst) == tst) src += " ROTARY_ENCODER";
+ s2 &= ~tst;
+ }
+ tst = InputDevice.SOURCE_STYLUS;
+ if ((s & tst) == tst) src += " STYLUS";
+ s2 &= ~tst;
+
+ tst = InputDevice.SOURCE_TOUCHPAD;
+ if ((s & tst) == tst) src += " TOUCHPAD";
+ s2 &= ~tst;
+
+ tst = InputDevice.SOURCE_TOUCHSCREEN;
+ if ((s & tst) == tst) src += " TOUCHSCREEN";
+ s2 &= ~tst;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ tst = InputDevice.SOURCE_TOUCH_NAVIGATION;
+ if ((s & tst) == tst) src += " TOUCH_NAVIGATION";
+ s2 &= ~tst;
+ }
+
+ tst = InputDevice.SOURCE_TRACKBALL;
+ if ((s & tst) == tst) src += " TRACKBALL";
+ s2 &= ~tst;
+
+ tst = InputDevice.SOURCE_ANY;
+ if ((s & tst) == tst) src += " ANY";
+ s2 &= ~tst;
+
+ if (s == FLAG_TAINTED) src += " FLAG_TAINTED";
+ s2 &= ~FLAG_TAINTED;
+
+ if (s2 != 0) src += " Some_Unkown";
+
+ Log.v(TAG, prefix + "int=" + s_copy + " CLASS={" + cls + " } source(s):" + src);
+ }
+*/
public static boolean mIsResumedCalled, mHasFocus;
public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24);
@@ 111,7 216,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
// Main components
protected static SDLActivity mSingleton;
protected static SDLSurface mSurface;
- protected static View mTextEdit;
+ protected static DummyEdit mTextEdit;
protected static boolean mScreenKeyboardShown;
protected static ViewGroup mLayout;
protected static SDLClipboardHandler mClipboardHandler;
@@ 212,10 317,17 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
mNextNativeState = NativeState.INIT;
mCurrentNativeState = NativeState.INIT;
}
+
+ protected SDLSurface createSDLSurface(Context context) {
+ return new SDLSurface(context);
+ }
// Setup
@Override
protected void onCreate(Bundle savedInstanceState) {
+ Log.v(TAG, "Device: " + Build.DEVICE);
+ Log.v(TAG, "Model: " + Build.MODEL);
+ Log.v(TAG, "onCreate()");
super.onCreate(savedInstanceState);
try {
@@ 239,8 351,18 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
errorMsgBrokenLib = e.getMessage();
}
- if (mBrokenLibraries)
- {
+ if (!mBrokenLibraries) {
+ String expected_version = String.valueOf(SDL_MAJOR_VERSION) + "." +
+ String.valueOf(SDL_MINOR_VERSION) + "." +
+ String.valueOf(SDL_MICRO_VERSION);
+ String version = nativeGetVersion();
+ if (!version.equals(expected_version)) {
+ mBrokenLibraries = true;
+ errorMsgBrokenLib = "SDL C/Java version mismatch (expected " + expected_version + ", got " + version + ")";
+ }
+ }
+
+ if (mBrokenLibraries) {
mSingleton = this;
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."
@@ 277,7 399,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
mHIDDeviceManager = HIDDeviceManager.acquire(this);
// Set up the surface
- mSurface = new SDLSurface(getApplication());
+ mSurface = createSDLSurface(getApplication());
mLayout = new RelativeLayout(this);
mLayout.addView(mSurface);
@@ 298,35 420,15 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
copyAssets();
setContentView(mLayout);
+
setWindowStyle(false);
+
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);
// Get filename from "Open with" of another application
onNewIntent(getIntent());
}
- protected void pauseNativeThread() {
- mNextNativeState = NativeState.PAUSED;
- mIsResumedCalled = false;
-
- if (SDLActivity.mBrokenLibraries) {
- return;
- }
-
- SDLActivity.handleNativeState();
- }
-
- protected void resumeNativeThread() {
- mNextNativeState = NativeState.RESUMED;
- mIsResumedCalled = true;
-
- if (SDLActivity.mBrokenLibraries) {
- return;
- }
-
- SDLActivity.handleNativeState();
- }
-
private void copyAssets() {
try {
AssetManager assets = getAssets();
@@ 355,6 457,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
@Override
protected void onNewIntent(Intent intent) {
+ if (intent == null)
+ return;
Uri uri = intent.getData();
if (uri == null)
return;
@@ 390,6 494,28 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
} catch (Exception e) {
Log.e(TAG, "Intent failed: " + e.getMessage());
}
+ }
+
+ protected void pauseNativeThread() {
+ mNextNativeState = NativeState.PAUSED;
+ mIsResumedCalled = false;
+
+ if (SDLActivity.mBrokenLibraries) {
+ return;
+ }
+
+ SDLActivity.handleNativeState();
+ }
+
+ protected void resumeNativeThread() {
+ mNextNativeState = NativeState.RESUMED;
+ mIsResumedCalled = true;
+
+ if (SDLActivity.mBrokenLibraries) {
+ return;
+ }
+
+ SDLActivity.handleNativeState();
}
// Events
@@ 838,6 964,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
}
// C functions we call
+ public static native String nativeGetVersion();
public static native int nativeSetupJNI();
public static native int nativeRunMain(String library, String function, Object arguments);
public static native void nativeLowMemory();
@@ 1172,8 1299,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()
- {
+ public static View getContentView() {
return mLayout;
}
@@ 1244,6 1370,77 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE;
}
+ public static boolean handleKeyEvent(View v, int keyCode, KeyEvent event, InputConnection ic) {
+ 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
+ //
+ // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
+ // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
+ // So, retrieve the device itself and check all of its sources
+ if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) {
+ // Note that we process events with specific key codes here
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) {
+ return true;
+ }
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) {
+ return true;
+ }
+ }
+ }
+
+ if ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (isTextInputEvent(event)) {
+ if (ic != null) {
+ ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
+ } else {
+ SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
+ }
+ }
+ onNativeKeyDown(keyCode);
+ return true;
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ onNativeKeyUp(keyCode);
+ return true;
+ }
+ }
+
+ if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
+ // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
+ // they are ignored here because sending them as mouse input to SDL is messy
+ if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
+ switch (event.getAction()) {
+ case KeyEvent.ACTION_DOWN:
+ case KeyEvent.ACTION_UP:
+ // mark the event as handled or it will be handled by system
+ // handling KEYCODE_BACK by system will call onBackPressed()
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
/**
* This method is called by SDL using JNI.
*/
@@ 1264,8 1461,21 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
for (int id : ids) {
InputDevice device = InputDevice.getDevice(id);
- if (device != null && (device.getSources() & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
- nativeAddTouch(device.getId(), device.getName());
+ /* Allow SOURCE_TOUCHSCREEN and also Virtual InputDevices because they can send TOUCHSCREEN events */
+ if (device != null && ((device.getSources() & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN
+ || device.isVirtual())) {
+
+ int touchDevId = device.getId();
+ /*
+ * 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;
+ }
+ nativeAddTouch(touchDevId, device.getName());
}
}
}
@@ 1734,14 1944,14 @@ class SDLMain implements Runnable {
try {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DISPLAY);
} catch (Exception e) {
- Log.v(SDLActivity.TAG, "modify thread properties failed " + e.toString());
+ Log.v("SDL", "modify thread properties failed " + e.toString());
}
- Log.v(SDLActivity.TAG, "Running main function " + function + " from library " + library);
+ Log.v("SDL", "Running main function " + function + " from library " + library);
SDLActivity.nativeRunMain(library, function, args);
- Log.v(SDLActivity.TAG, "Finished main function");
+ Log.v("SDL", "Finished main function");
if (SDLActivity.mSingleton != null && !SDLActivity.mSingleton.isFinishing()) {
// Let's finish the Activity
@@ 1752,455 1962,6 @@ class SDLMain implements Runnable {
}
}
-
-/**
- SDLSurface. This is what we draw on, so we need to know when it's created
- in order to do anything useful.
-
- Because of this, that's where we set up the SDL thread
-*/
-class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
- View.OnKeyListener, View.OnTouchListener, SensorEventListener {
-
- // Sensors
- protected SensorManager mSensorManager;
- protected Display mDisplay;
-
- // Keep track of the surface size to normalize touch events
- protected float mWidth, mHeight;
-
- // Is SurfaceView ready for rendering
- public boolean mIsSurfaceReady;
-
- // Startup
- public SDLSurface(Context context) {
- super(context);
- getHolder().addCallback(this);
-
- setFocusable(true);
- setFocusableInTouchMode(true);
- requestFocus();
- setOnKeyListener(this);
- setOnTouchListener(this);
-
- mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
- mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
-
- setOnGenericMotionListener(SDLActivity.getMotionListener());
-
- // Some arbitrary defaults to avoid a potential division by zero
- mWidth = 1.0f;
- mHeight = 1.0f;
-
- mIsSurfaceReady = false;
- }
-
- public void handlePause() {
- enableSensor(Sensor.TYPE_ACCELEROMETER, false);
- }
-
- public void handleResume() {
- setFocusable(true);
- setFocusableInTouchMode(true);
- requestFocus();
- setOnKeyListener(this);
- setOnTouchListener(this);
- enableSensor(Sensor.TYPE_ACCELEROMETER, true);
- }
-
- public Surface getNativeSurface() {
- return getHolder().getSurface();
- }
-
- // Called when we have a valid drawing surface
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- Log.v(SDLActivity.TAG, "surfaceCreated()");
- SDLActivity.onNativeSurfaceCreated();
- }
-
- // Called when we lose the surface
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- Log.v(SDLActivity.TAG, "surfaceDestroyed()");
-
- // Transition to pause, if needed
- SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
- SDLActivity.handleNativeState();
-
- mIsSurfaceReady = false;
- SDLActivity.onNativeSurfaceDestroyed();
- }
-
- // Called when the surface is resized
- @Override
- public void surfaceChanged(SurfaceHolder holder,
- int format, int width, int height) {
- Log.v(SDLActivity.TAG, "surfaceChanged()");
-
- if (SDLActivity.mSingleton == null) {
- return;
- }
-
- mWidth = width;
- mHeight = height;
- int nDeviceWidth = width;
- int nDeviceHeight = height;
- try
- {
- if (Build.VERSION.SDK_INT >= 17) {
- DisplayMetrics realMetrics = new DisplayMetrics();
- mDisplay.getRealMetrics( realMetrics );
- nDeviceWidth = realMetrics.widthPixels;
- nDeviceHeight = realMetrics.heightPixels;
- }
- } catch(Exception ignored) {
- }
-
- synchronized(SDLActivity.getContext()) {
- // In case we're waiting on a size change after going fullscreen, send a notification.
- SDLActivity.getContext().notifyAll();
- }
-
- Log.v(SDLActivity.TAG, "Window size: " + width + "x" + height);
- Log.v(SDLActivity.TAG, "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
- SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
- SDLActivity.onNativeResize();
-
- // Prevent a screen distortion glitch,
- // for instance when the device is in Landscape and a Portrait App is resumed.
- boolean skip = false;
- int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
-
- if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
- if (mWidth > mHeight) {
- skip = true;
- }
- } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
- if (mWidth < mHeight) {
- skip = true;
- }
- }
-
- // Special Patch for Square Resolution: Black Berry Passport
- if (skip) {
- double min = Math.min(mWidth, mHeight);
- double max = Math.max(mWidth, mHeight);
-
- if (max / min < 1.20) {
- Log.v(SDLActivity.TAG, "Don't skip on such aspect-ratio. Could be a square resolution.");
- skip = false;
- }
- }
-
- // Don't skip in MultiWindow.
- if (skip) {
- if (Build.VERSION.SDK_INT >= 24) {
- if (SDLActivity.mSingleton.isInMultiWindowMode()) {
- Log.v(SDLActivity.TAG, "Don't skip in Multi-Window");
- skip = false;
- }
- }
- }
-
- if (skip) {
- Log.v(SDLActivity.TAG, "Skip .. Surface is not ready.");
- mIsSurfaceReady = false;
- return;
- }
-
- /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
- SDLActivity.onNativeSurfaceChanged();
-
- /* Surface is ready */
- mIsSurfaceReady = true;
-
- SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
- SDLActivity.handleNativeState();
- }
-
- // Key events
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
-
- 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(SDLActivity.TAG, "key down: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
- } else if (event.getAction() == KeyEvent.ACTION_UP) {
- Log.v(SDLActivity.TAG, "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
- //
- // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
- // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
- // So, retrieve the device itself and check all of its sources
- if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) {
- // Note that we process events with specific key codes here
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) {
- return true;
- }
- } else if (event.getAction() == KeyEvent.ACTION_UP) {
- if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) {
- return true;
- }
- }
- }
-
- if ((source & InputDevice.SOURCE_KEYBOARD) != 0) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- if (SDLActivity.isTextInputEvent(event)) {
- SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
- }
- SDLActivity.onNativeKeyDown(keyCode);
- return true;
- } else if (event.getAction() == KeyEvent.ACTION_UP) {
- SDLActivity.onNativeKeyUp(keyCode);
- return true;
- }
- }
-
- if ((source & InputDevice.SOURCE_MOUSE) != 0) {
- // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
- // they are ignored here because sending them as mouse input to SDL is messy
- if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
- switch (event.getAction()) {
- case KeyEvent.ACTION_DOWN:
- case KeyEvent.ACTION_UP:
- // mark the event as handled or it will be handled by system
- // handling KEYCODE_BACK by system will call onBackPressed()
- return true;
- }
- }
- }
-
- return false;
- }
-
- // Touch events
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- /* Ref: http://developer.android.com/training/gestures/multi.html */
- int touchDevId = event.getDeviceId();
- final int pointerCount = event.getPointerCount();
- int action = event.getActionMasked();
- int pointerFingerId;
- 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 {
- 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
- // if we are. We'll leverage our existing mouse motion listener
- SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
- x = motionListener.getEventX(event);
- y = motionListener.getEventY(event);
-
- SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
- } else {
- switch(action) {
- case MotionEvent.ACTION_MOVE:
- for (i = 0; i < pointerCount; i++) {
- pointerFingerId = event.getPointerId(i);
- x = event.getX(i) / mWidth;
- y = event.getY(i) / mHeight;
- p = event.getPressure(i);
- if (p > 1.0f) {
- // may be larger than 1.0f on some devices
- // see the documentation of getPressure(i)
- p = 1.0f;
- }
- SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
- }
- break;
-
- case MotionEvent.ACTION_UP:
- 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
- if (i == -1) {
- i = event.getActionIndex();
- }
-
- pointerFingerId = event.getPointerId(i);
- x = event.getX(i) / mWidth;
- y = event.getY(i) / mHeight;
- p = event.getPressure(i);
- if (p > 1.0f) {
- // may be larger than 1.0f on some devices
- // see the documentation of getPressure(i)
- p = 1.0f;
- }
- SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
- break;
-
- case MotionEvent.ACTION_CANCEL:
- for (i = 0; i < pointerCount; i++) {
- pointerFingerId = event.getPointerId(i);
- x = event.getX(i) / mWidth;
- y = event.getY(i) / mHeight;
- p = event.getPressure(i);
- if (p > 1.0f) {
- // may be larger than 1.0f on some devices
- // see the documentation of getPressure(i)
- p = 1.0f;
- }
- SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
- }
- break;
-
- default:
- break;
- }
- }
-
- return true;
- }
-
- // Sensor events
- public void enableSensor(int sensortype, boolean enabled) {
- // TODO: This uses getDefaultSensor - what if we have >1 accels?
- if (enabled) {
- mSensorManager.registerListener(this,
- mSensorManager.getDefaultSensor(sensortype),
- SensorManager.SENSOR_DELAY_GAME, null);
- } else {
- mSensorManager.unregisterListener(this,
- mSensorManager.getDefaultSensor(sensortype));
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // TODO
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
-
- // Since we may have an orientation set, we won't receive onConfigurationChanged events.
- // We thus should check here.
- int newOrientation;
-
- float x, y;
- switch (mDisplay.getRotation()) {
- case Surface.ROTATION_90:
- x = -event.values[1];
- y = event.values[0];
- newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
- break;
- case Surface.ROTATION_270:
- x = event.values[1];
- y = -event.values[0];
- newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
- break;
- case Surface.ROTATION_180:
- x = -event.values[0];
- y = -event.values[1];
- newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
- break;
- case Surface.ROTATION_0:
- default:
- x = event.values[0];
- y = event.values[1];
- newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
- break;
- }
-
- if (newOrientation != SDLActivity.mCurrentOrientation) {
- SDLActivity.mCurrentOrientation = newOrientation;
- SDLActivity.onNativeOrientationChanged(newOrientation);
- }
-
- SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
- y / SensorManager.GRAVITY_EARTH,
- event.values[2] / SensorManager.GRAVITY_EARTH);
-
-
- }
- }
-
- // Captured pointer events for API 26.
- public boolean onCapturedPointerEvent(MotionEvent event)
- {
- int action = event.getActionMasked();
-
- float x, y;
- switch (action) {
- case MotionEvent.ACTION_SCROLL:
- x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
- y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
- SDLActivity.onNativeMouse(0, action, x, y, false);
- return true;
-
- case MotionEvent.ACTION_HOVER_MOVE:
- case MotionEvent.ACTION_MOVE:
- x = event.getX(0);
- y = event.getY(0);
- SDLActivity.onNativeMouse(0, action, x, y, true);
- return true;
-
- case MotionEvent.ACTION_BUTTON_PRESS:
- case MotionEvent.ACTION_BUTTON_RELEASE:
-
- // Change our action value to what SDL's code expects.
- if (action == MotionEvent.ACTION_BUTTON_PRESS) {
- action = MotionEvent.ACTION_DOWN;
- } else { /* MotionEvent.ACTION_BUTTON_RELEASE */
- action = MotionEvent.ACTION_UP;
- }
-
- x = event.getX(0);
- y = event.getY(0);
- int button = event.getButtonState();
-
- SDLActivity.onNativeMouse(button, action, x, y, true);
- return true;
- }
-
- return false;
- }
-
-}
-
/* This is a fake invisible editor view that receives the input and defines the
* pan&scan region
*/
@@ 2221,22 1982,7 @@ class DummyEdit extends View implements View.OnKeyListener {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
- //Log.v(SDLActivity.TAG, "onKey " + keyCode + " " + event);
- /*
- * This handles the hardware keyboard input
- */
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- if (SDLActivity.isTextInputEvent(event)) {
- ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
- return true;
- }
- SDLActivity.onNativeKeyDown(keyCode);
- return true;
- } else if (event.getAction() == KeyEvent.ACTION_UP) {
- SDLActivity.onNativeKeyUp(keyCode);
- return true;
- }
- return false;
+ return SDLActivity.handleKeyEvent(v, keyCode, event, ic);
}
//
@@ 2260,9 2006,10 @@ class DummyEdit extends View implements View.OnKeyListener {
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
ic = new SDLInputConnection(this, true);
- outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
- outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
- | EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
+ outAttrs.inputType = InputType.TYPE_CLASS_TEXT |
+ InputType.TYPE_TEXT_FLAG_MULTI_LINE;
+ outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI |
+ EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
return ic;
}
@@ 2270,9 2017,17 @@ class DummyEdit extends View implements View.OnKeyListener {
class SDLInputConnection extends BaseInputConnection {
+ protected EditText mEditText;
+ protected String mCommittedText = "";
+
public SDLInputConnection(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
+ mEditText = new EditText(SDL.getContext());
+ }
+ @Override
+ public Editable getEditable() {
+ return mEditText.getEditableText();
}
@Override
@@ 2295,59 2050,84 @@ class SDLInputConnection extends BaseInputConnection {
}
}
-
return super.sendKeyEvent(event);
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
-
- for (int i = 0; i < text.length(); i++) {
- char c = text.charAt(i);
- if (c == '\n') {
- if (SDLActivity.onNativeSoftReturnKey()) {
- return true;
- }
- }
- nativeGenerateScancodeForUnichar(c);
+ if (!super.commitText(text, newCursorPosition)) {
+ return false;
}
-
- SDLInputConnection.nativeCommitText(text.toString(), newCursorPosition);
-
- return super.commitText(text, newCursorPosition);
+ updateText();
+ return true;
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
-
- nativeSetComposingText(text.toString(), newCursorPosition);
-
- return super.setComposingText(text, newCursorPosition);
+ if (!super.setComposingText(text, newCursorPosition)) {
+ return false;
+ }
+ updateText();
+ return true;
}
- public static native void nativeCommitText(String text, int newCursorPosition);
+ @Override
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ if (!super.deleteSurroundingText(beforeLength, afterLength)) {
+ return false;
+ }
+ updateText();
+ return true;
+ }
- public native void nativeGenerateScancodeForUnichar(char c);
+ protected void updateText() {
+ final Editable content = getEditable();
+ if (content == null) {
+ return;
+ }
- public native void nativeSetComposingText(String text, int newCursorPosition);
+ String text = content.toString();
+ int compareLength = Math.min(text.length(), mCommittedText.length());
+ int matchLength, offset;
- @Override
- public boolean deleteSurroundingText(int beforeLength, int afterLength) {
- // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
- // and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
- if (beforeLength > 0 && afterLength == 0) {
- boolean ret = true;
- // backspace(s)
- while (beforeLength-- > 0) {
- boolean ret_key = sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
- && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
- ret = ret && ret_key;
+ /* Backspace over characters that are no longer in the string */
+ for (matchLength = 0; matchLength < compareLength; ) {
+ int codePoint = mCommittedText.codePointAt(matchLength);
+ if (codePoint != text.codePointAt(matchLength)) {
+ break;
+ }
+ matchLength += Character.charCount(codePoint);
+ }
+ /* FIXME: This doesn't handle graphemes, like '🌬️' */
+ for (offset = matchLength; offset < mCommittedText.length(); ) {
+ int codePoint = mCommittedText.codePointAt(offset);
+ nativeGenerateScancodeForUnichar('\b');
+ offset += Character.charCount(codePoint);
+ }
+
+ if (matchLength < text.length()) {
+ String pendingText = text.subSequence(matchLength, text.length()).toString();
+ for (offset = 0; offset < pendingText.length(); ) {
+ int codePoint = pendingText.codePointAt(offset);
+ if (codePoint == '\n') {
+ if (SDLActivity.onNativeSoftReturnKey()) {
+ return;
+ }
+ }
+ /* Higher code points don't generate simulated scancodes */
+ if (codePoint < 128) {
+ nativeGenerateScancodeForUnichar((char)codePoint);
+ }
+ offset += Character.charCount(codePoint);
}
- return ret;
+ SDLInputConnection.nativeCommitText(pendingText, 0);
}
-
- return super.deleteSurroundingText(beforeLength, afterLength);
+ mCommittedText = text;
}
+
+ public static native void nativeCommitText(String text, int newCursorPosition);
+
+ public static native void nativeGenerateScancodeForUnichar(char c);
}
class SDLClipboardHandler implements
@@ 2390,3 2170,4 @@ class SDLClipboardHandler implements
SDLActivity.onNativeClipboardChanged();
}
}
+
M src/android/app/src/main/java/org/libsdl/app/SDLControllerManager.java => src/android/app/src/main/java/org/libsdl/app/SDLControllerManager.java +17 -21
@@ 255,23 255,21 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler {
@Override
public boolean handleMotionEvent(MotionEvent event) {
- if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
- int actionPointerIndex = event.getActionIndex();
- int action = event.getActionMasked();
- 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);
- }
+ int actionPointerIndex = event.getActionIndex();
+ int action = event.getActionMasked();
+ 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);
}
}
}
@@ 319,6 317,7 @@ class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
KeyEvent.KEYCODE_BUTTON_X,
KeyEvent.KEYCODE_BUTTON_Y,
KeyEvent.KEYCODE_BACK,
+ KeyEvent.KEYCODE_MENU,
KeyEvent.KEYCODE_BUTTON_MODE,
KeyEvent.KEYCODE_BUTTON_START,
KeyEvent.KEYCODE_BUTTON_THUMBL,
@@ 360,6 359,7 @@ class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
(1 << 2), // X -> X
(1 << 3), // Y -> Y
(1 << 4), // BACK -> BACK
+ (1 << 6), // MENU -> START
(1 << 5), // MODE -> GUIDE
(1 << 6), // START -> START
(1 << 7), // THUMBL -> LEFTSTICK
@@ 560,8 560,6 @@ class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
- case InputDevice.SOURCE_GAMEPAD:
- case InputDevice.SOURCE_DPAD:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
@@ 691,8 689,6 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
- case InputDevice.SOURCE_GAMEPAD:
- case InputDevice.SOURCE_DPAD:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
A src/android/app/src/main/java/org/libsdl/app/SDLSurface.java => src/android/app/src/main/java/org/libsdl/app/SDLSurface.java +405 -0
@@ 0,0 1,405 @@
+package org.libsdl.app;
+
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.WindowManager;
+
+
+/**
+ SDLSurface. This is what we draw on, so we need to know when it's created
+ in order to do anything useful.
+
+ Because of this, that's where we set up the SDL thread
+*/
+public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
+ View.OnKeyListener, View.OnTouchListener, SensorEventListener {
+
+ // Sensors
+ protected SensorManager mSensorManager;
+ protected Display mDisplay;
+
+ // Keep track of the surface size to normalize touch events
+ protected float mWidth, mHeight;
+
+ // Is SurfaceView ready for rendering
+ public boolean mIsSurfaceReady;
+
+ // Startup
+ public SDLSurface(Context context) {
+ super(context);
+ getHolder().addCallback(this);
+
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ requestFocus();
+ setOnKeyListener(this);
+ setOnTouchListener(this);
+
+ mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+ mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+
+ setOnGenericMotionListener(SDLActivity.getMotionListener());
+
+ // Some arbitrary defaults to avoid a potential division by zero
+ mWidth = 1.0f;
+ mHeight = 1.0f;
+
+ mIsSurfaceReady = false;
+ }
+
+ public void handlePause() {
+ enableSensor(Sensor.TYPE_ACCELEROMETER, false);
+ }
+
+ public void handleResume() {
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ requestFocus();
+ setOnKeyListener(this);
+ setOnTouchListener(this);
+ enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+ }
+
+ public Surface getNativeSurface() {
+ return getHolder().getSurface();
+ }
+
+ // Called when we have a valid drawing surface
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ Log.v("SDL", "surfaceCreated()");
+ SDLActivity.onNativeSurfaceCreated();
+ }
+
+ // Called when we lose the surface
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.v("SDL", "surfaceDestroyed()");
+
+ // Transition to pause, if needed
+ SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
+ SDLActivity.handleNativeState();
+
+ mIsSurfaceReady = false;
+ SDLActivity.onNativeSurfaceDestroyed();
+ }
+
+ // Called when the surface is resized
+ @Override
+ public void surfaceChanged(SurfaceHolder holder,
+ int format, int width, int height) {
+ Log.v("SDL", "surfaceChanged()");
+
+ if (SDLActivity.mSingleton == null) {
+ return;
+ }
+
+ mWidth = width;
+ mHeight = height;
+ int nDeviceWidth = width;
+ int nDeviceHeight = height;
+ try
+ {
+ if (Build.VERSION.SDK_INT >= 17) {
+ DisplayMetrics realMetrics = new DisplayMetrics();
+ mDisplay.getRealMetrics( realMetrics );
+ nDeviceWidth = realMetrics.widthPixels;
+ nDeviceHeight = realMetrics.heightPixels;
+ }
+ } catch(Exception ignored) {
+ }
+
+ synchronized(SDLActivity.getContext()) {
+ // In case we're waiting on a size change after going fullscreen, send a notification.
+ SDLActivity.getContext().notifyAll();
+ }
+
+ Log.v("SDL", "Window size: " + width + "x" + height);
+ Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
+ SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
+ SDLActivity.onNativeResize();
+
+ // Prevent a screen distortion glitch,
+ // for instance when the device is in Landscape and a Portrait App is resumed.
+ boolean skip = false;
+ int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
+
+ if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
+ if (mWidth > mHeight) {
+ skip = true;
+ }
+ } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
+ if (mWidth < mHeight) {
+ skip = true;
+ }
+ }
+
+ // Special Patch for Square Resolution: Black Berry Passport
+ if (skip) {
+ double min = Math.min(mWidth, mHeight);
+ double max = Math.max(mWidth, mHeight);
+
+ if (max / min < 1.20) {
+ Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
+ skip = false;
+ }
+ }
+
+ // Don't skip in MultiWindow.
+ if (skip) {
+ if (Build.VERSION.SDK_INT >= 24) {
+ if (SDLActivity.mSingleton.isInMultiWindowMode()) {
+ Log.v("SDL", "Don't skip in Multi-Window");
+ skip = false;
+ }
+ }
+ }
+
+ if (skip) {
+ Log.v("SDL", "Skip .. Surface is not ready.");
+ mIsSurfaceReady = false;
+ return;
+ }
+
+ /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
+ SDLActivity.onNativeSurfaceChanged();
+
+ /* Surface is ready */
+ mIsSurfaceReady = true;
+
+ SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
+ SDLActivity.handleNativeState();
+ }
+
+ // Key events
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ return SDLActivity.handleKeyEvent(v, keyCode, event, null);
+ }
+
+ // Touch events
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ /* Ref: http://developer.android.com/training/gestures/multi.html */
+ int touchDevId = event.getDeviceId();
+ final int pointerCount = event.getPointerCount();
+ int action = event.getActionMasked();
+ int pointerFingerId;
+ 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 {
+ 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
+ // if we are. We'll leverage our existing mouse motion listener
+ SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
+ x = motionListener.getEventX(event);
+ y = motionListener.getEventY(event);
+
+ SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
+ } else {
+ switch(action) {
+ case MotionEvent.ACTION_MOVE:
+ for (i = 0; i < pointerCount; i++) {
+ pointerFingerId = event.getPointerId(i);
+ x = event.getX(i) / mWidth;
+ y = event.getY(i) / mHeight;
+ p = event.getPressure(i);
+ if (p > 1.0f) {
+ // may be larger than 1.0f on some devices
+ // see the documentation of getPressure(i)
+ p = 1.0f;
+ }
+ SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ 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
+ if (i == -1) {
+ i = event.getActionIndex();
+ }
+
+ pointerFingerId = event.getPointerId(i);
+ x = event.getX(i) / mWidth;
+ y = event.getY(i) / mHeight;
+ p = event.getPressure(i);
+ if (p > 1.0f) {
+ // may be larger than 1.0f on some devices
+ // see the documentation of getPressure(i)
+ p = 1.0f;
+ }
+ SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ for (i = 0; i < pointerCount; i++) {
+ pointerFingerId = event.getPointerId(i);
+ x = event.getX(i) / mWidth;
+ y = event.getY(i) / mHeight;
+ p = event.getPressure(i);
+ if (p > 1.0f) {
+ // may be larger than 1.0f on some devices
+ // see the documentation of getPressure(i)
+ p = 1.0f;
+ }
+ SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ // Sensor events
+ public void enableSensor(int sensortype, boolean enabled) {
+ // TODO: This uses getDefaultSensor - what if we have >1 accels?
+ if (enabled) {
+ mSensorManager.registerListener(this,
+ mSensorManager.getDefaultSensor(sensortype),
+ SensorManager.SENSOR_DELAY_GAME, null);
+ } else {
+ mSensorManager.unregisterListener(this,
+ mSensorManager.getDefaultSensor(sensortype));
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // TODO
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
+
+ // Since we may have an orientation set, we won't receive onConfigurationChanged events.
+ // We thus should check here.
+ int newOrientation;
+
+ float x, y;
+ switch (mDisplay.getRotation()) {
+ case Surface.ROTATION_90:
+ x = -event.values[1];
+ y = event.values[0];
+ newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
+ break;
+ case Surface.ROTATION_270:
+ x = event.values[1];
+ y = -event.values[0];
+ newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
+ break;
+ case Surface.ROTATION_180:
+ x = -event.values[0];
+ y = -event.values[1];
+ newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
+ break;
+ case Surface.ROTATION_0:
+ default:
+ x = event.values[0];
+ y = event.values[1];
+ newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
+ break;
+ }
+
+ if (newOrientation != SDLActivity.mCurrentOrientation) {
+ SDLActivity.mCurrentOrientation = newOrientation;
+ SDLActivity.onNativeOrientationChanged(newOrientation);
+ }
+
+ SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
+ y / SensorManager.GRAVITY_EARTH,
+ event.values[2] / SensorManager.GRAVITY_EARTH);
+
+
+ }
+ }
+
+ // Captured pointer events for API 26.
+ public boolean onCapturedPointerEvent(MotionEvent event)
+ {
+ int action = event.getActionMasked();
+
+ float x, y;
+ switch (action) {
+ case MotionEvent.ACTION_SCROLL:
+ x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
+ y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
+ SDLActivity.onNativeMouse(0, action, x, y, false);
+ return true;
+
+ case MotionEvent.ACTION_HOVER_MOVE:
+ case MotionEvent.ACTION_MOVE:
+ x = event.getX(0);
+ y = event.getY(0);
+ SDLActivity.onNativeMouse(0, action, x, y, true);
+ return true;
+
+ case MotionEvent.ACTION_BUTTON_PRESS:
+ case MotionEvent.ACTION_BUTTON_RELEASE:
+
+ // Change our action value to what SDL's code expects.
+ if (action == MotionEvent.ACTION_BUTTON_PRESS) {
+ action = MotionEvent.ACTION_DOWN;
+ } else { /* MotionEvent.ACTION_BUTTON_RELEASE */
+ action = MotionEvent.ACTION_UP;
+ }
+
+ x = event.getX(0);
+ y = event.getY(0);
+ int button = event.getButtonState();
+
+ SDLActivity.onNativeMouse(button, action, x, y, true);
+ return true;
+ }
+
+ return false;
+ }
+}
M src/android/build.gradle => src/android/build.gradle +1 -1
@@ 15,7 15,7 @@ buildscript {
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.0.4'
+ classpath 'com.android.tools.build:gradle:7.4.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
M src/android/gradle/wrapper/gradle-wrapper.properties => src/android/gradle/wrapper/gradle-wrapper.properties +3 -3
@@ 1,6 1,6 @@
-#Thu Nov 11 18:20:34 PST 2021
+#Mon Feb 13 14:40:52 CET 2023
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
distributionPath=wrapper/dists
-zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists