diff options
Diffstat (limited to 'platform/android/java/lib/src')
11 files changed, 298 insertions, 206 deletions
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index ce53aeebcb..290be727ab 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -38,7 +38,6 @@ import android.content.pm.PackageManager import android.content.res.Configuration import android.content.res.Resources import android.graphics.Color -import android.graphics.Rect import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener @@ -46,10 +45,12 @@ import android.hardware.SensorManager import android.os.* import android.util.Log import android.view.* -import android.view.ViewTreeObserver.OnGlobalLayoutListener import android.widget.FrameLayout import androidx.annotation.Keep import androidx.annotation.StringRes +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsAnimationCompat +import androidx.core.view.WindowInsetsCompat import com.google.android.vending.expansion.downloader.* import org.godotengine.godot.input.GodotEditText import org.godotengine.godot.io.directory.DirectoryAccessHandler @@ -418,58 +419,42 @@ class Godot(private val context: Context) : SensorEventListener { io?.setEdit(editText) // Listeners for keyboard height. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - // Report the height of virtual keyboard as it changes during the animation. - val decorView = activity.window.decorView - decorView.setWindowInsetsAnimationCallback(object : WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { - var startBottom = 0 - var endBottom = 0 - override fun onPrepare(animation: WindowInsetsAnimation) { - startBottom = decorView.rootWindowInsets.getInsets(WindowInsets.Type.ime()).bottom - } + val decorView = activity.window.decorView + // Report the height of virtual keyboard as it changes during the animation. + ViewCompat.setWindowInsetsAnimationCallback(decorView, object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) { + var startBottom = 0 + var endBottom = 0 + override fun onPrepare(animation: WindowInsetsAnimationCompat) { + startBottom = ViewCompat.getRootWindowInsets(decorView)?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0 + } - override fun onStart(animation: WindowInsetsAnimation, bounds: WindowInsetsAnimation.Bounds): WindowInsetsAnimation.Bounds { - endBottom = decorView.rootWindowInsets.getInsets(WindowInsets.Type.ime()).bottom - return bounds - } + override fun onStart(animation: WindowInsetsAnimationCompat, bounds: WindowInsetsAnimationCompat.BoundsCompat): WindowInsetsAnimationCompat.BoundsCompat { + endBottom = ViewCompat.getRootWindowInsets(decorView)?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0 + return bounds + } - override fun onProgress(windowInsets: WindowInsets, list: List<WindowInsetsAnimation>): WindowInsets { - // Find the IME animation. - var imeAnimation: WindowInsetsAnimation? = null - for (animation in list) { - if (animation.typeMask and WindowInsets.Type.ime() != 0) { - imeAnimation = animation - break - } + override fun onProgress(windowInsets: WindowInsetsCompat, animationsList: List<WindowInsetsAnimationCompat>): WindowInsetsCompat { + // Find the IME animation. + var imeAnimation: WindowInsetsAnimationCompat? = null + for (animation in animationsList) { + if (animation.typeMask and WindowInsetsCompat.Type.ime() != 0) { + imeAnimation = animation + break } - // Update keyboard height based on IME animation. - if (imeAnimation != null) { - val interpolatedFraction = imeAnimation.interpolatedFraction - // Linear interpolation between start and end values. - val keyboardHeight = startBottom * (1.0f - interpolatedFraction) + endBottom * interpolatedFraction - GodotLib.setVirtualKeyboardHeight(keyboardHeight.toInt()) - } - return windowInsets } - override fun onEnd(animation: WindowInsetsAnimation) {} - }) - } else { - // Infer the virtual keyboard height using visible area. - renderView?.view?.viewTreeObserver?.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { - // Don't allocate a new Rect every time the callback is called. - val visibleSize = Rect() - override fun onGlobalLayout() { - renderView?.let { - val surfaceView = it.view - - surfaceView.getWindowVisibleDisplayFrame(visibleSize) - val keyboardHeight = surfaceView.height - visibleSize.bottom - GodotLib.setVirtualKeyboardHeight(keyboardHeight) - } + // Update keyboard height based on IME animation. + if (imeAnimation != null) { + val interpolatedFraction = imeAnimation.interpolatedFraction + // Linear interpolation between start and end values. + val keyboardHeight = startBottom * (1.0f - interpolatedFraction) + endBottom * interpolatedFraction + GodotLib.setVirtualKeyboardHeight(keyboardHeight.toInt()) } - }) - } + return windowInsets + } + + override fun onEnd(animation: WindowInsetsAnimationCompat) {} + }) if (host == primaryHost) { renderView?.queueOnRenderThread { @@ -894,16 +879,25 @@ class Godot(private val context: Context) : SensorEventListener { */ @SuppressLint("MissingPermission") @Keep - private fun vibrate(durationMs: Int) { + private fun vibrate(durationMs: Int, amplitude: Int) { if (durationMs > 0 && requestPermission("VIBRATE")) { val vibratorService = getActivity()?.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator? ?: return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - vibratorService.vibrate( - VibrationEffect.createOneShot( - durationMs.toLong(), - VibrationEffect.DEFAULT_AMPLITUDE + if (amplitude <= -1) { + vibratorService.vibrate( + VibrationEffect.createOneShot( + durationMs.toLong(), + VibrationEffect.DEFAULT_AMPLITUDE + ) ) - ) + } else { + vibratorService.vibrate( + VibrationEffect.createOneShot( + durationMs.toLong(), + amplitude + ) + ) + } } else { // deprecated in API 26 vibratorService.vibrate(durationMs.toLong()) diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt index 7b8fad8952..4c5e857b7a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt @@ -36,6 +36,7 @@ import android.content.pm.PackageManager import android.os.Bundle import android.util.Log import androidx.annotation.CallSuper +import androidx.annotation.LayoutRes import androidx.fragment.app.FragmentActivity import org.godotengine.godot.utils.PermissionsUtil import org.godotengine.godot.utils.ProcessPhoenix @@ -65,7 +66,7 @@ abstract class GodotActivity : FragmentActivity(), GodotHost { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.godot_app_layout) + setContentView(getGodotAppLayout()) handleStartIntent(intent, true) @@ -80,6 +81,9 @@ abstract class GodotActivity : FragmentActivity(), GodotHost { } } + @LayoutRes + protected open fun getGodotAppLayout() = R.layout.godot_app_layout + override fun onDestroy() { Log.v(TAG, "Destroying Godot app...") super.onDestroy() diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java index bd8c58ad69..c316812404 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java @@ -1955,4 +1955,3 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private int mEGLContextClientVersion; private boolean mPreserveEGLContextOnPause; } - diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index dc8a0e54bb..c085bb8886 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java @@ -266,8 +266,13 @@ public class GodotEditText extends EditText { boolean hasHardwareKeyboard() { Configuration config = getResources().getConfiguration(); - return config.keyboard != Configuration.KEYBOARD_NOKEYS && + boolean hasHardwareKeyboardConfig = config.keyboard != Configuration.KEYBOARD_NOKEYS && config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; + if (hasHardwareKeyboardConfig) { + return true; + } + + return mRenderView.getInputHandler().hasHardwareKeyboard(); } // =========================================================== diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt index 89fbb9f580..49b34a5229 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt @@ -61,8 +61,11 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi private var contextClickInProgress = false private var pointerCaptureInProgress = false + private var lastDragX: Float = 0.0f + private var lastDragY: Float = 0.0f + override fun onDown(event: MotionEvent): Boolean { - GodotInputHandler.handleMotionEvent(event.source, MotionEvent.ACTION_DOWN, event.buttonState, event.x, event.y, nextDownIsDoubleTap) + GodotInputHandler.handleMotionEvent(event, MotionEvent.ACTION_DOWN, nextDownIsDoubleTap) nextDownIsDoubleTap = false return true } @@ -82,20 +85,14 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi } // Cancel the previous down event - GodotInputHandler.handleMotionEvent( - event.source, - MotionEvent.ACTION_CANCEL, - event.buttonState, - event.x, - event.y - ) + GodotInputHandler.handleMotionEvent(event, MotionEvent.ACTION_CANCEL) // Turn a context click into a single tap right mouse button click. GodotInputHandler.handleMouseEvent( + event, MotionEvent.ACTION_DOWN, MotionEvent.BUTTON_SECONDARY, - event.x, - event.y + false ) contextClickInProgress = true } @@ -107,16 +104,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi if (!hasCapture) { // Dispatch a mouse relative ACTION_UP event to signal the end of the capture - GodotInputHandler.handleMouseEvent( - MotionEvent.ACTION_UP, - 0, - 0f, - 0f, - 0f, - 0f, - false, - true - ) + GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_UP, true) } pointerCaptureInProgress = hasCapture } @@ -139,32 +127,19 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi return true } - val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE) - } else { - false - } - if (pointerCaptureInProgress || dragInProgress || contextClickInProgress) { if (contextClickInProgress || GodotInputHandler.isMouseEvent(event)) { // This may be an ACTION_BUTTON_RELEASE event which we don't handle, // so we convert it to an ACTION_UP event. - GodotInputHandler.handleMouseEvent( - MotionEvent.ACTION_UP, - event.buttonState, - event.x, - event.y, - 0f, - 0f, - false, - sourceMouseRelative - ) + GodotInputHandler.handleMouseEvent(event, MotionEvent.ACTION_UP) } else { GodotInputHandler.handleTouchEvent(event) } pointerCaptureInProgress = false dragInProgress = false contextClickInProgress = false + lastDragX = 0.0f + lastDragY = 0.0f return true } @@ -173,22 +148,19 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi private fun onActionMove(event: MotionEvent): Boolean { if (contextClickInProgress) { - val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE) - } else { - false - } - GodotInputHandler.handleMouseEvent( - event.actionMasked, - MotionEvent.BUTTON_SECONDARY, - event.x, - event.y, - 0f, - 0f, - false, - sourceMouseRelative - ) + GodotInputHandler.handleMouseEvent(event, event.actionMasked, MotionEvent.BUTTON_SECONDARY, false) return true + } else if (!scaleInProgress) { + // The 'onScroll' event is triggered with a long delay. + // Force the 'InputEventScreenDrag' event earlier here. + // We don't toggle 'dragInProgress' here so that the scaling logic can override the drag operation if needed. + // Once the 'onScroll' event kicks-in, 'dragInProgress' will be properly set. + if (lastDragX != event.getX(0) || lastDragY != event.getY(0)) { + lastDragX = event.getX(0) + lastDragY = event.getY(0) + GodotInputHandler.handleMotionEvent(event) + return true + } } return false } @@ -197,7 +169,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi if (event.actionMasked == MotionEvent.ACTION_UP) { nextDownIsDoubleTap = false GodotInputHandler.handleMotionEvent(event) - } else if (event.actionMasked == MotionEvent.ACTION_MOVE && panningAndScalingEnabled == false) { + } else if (event.actionMasked == MotionEvent.ACTION_MOVE && !panningAndScalingEnabled) { GodotInputHandler.handleMotionEvent(event) } @@ -216,18 +188,14 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi distanceY: Float ): Boolean { if (scaleInProgress) { - if (dragInProgress) { + if (dragInProgress || lastDragX != 0.0f || lastDragY != 0.0f) { if (originEvent != null) { // Cancel the drag - GodotInputHandler.handleMotionEvent( - originEvent.source, - MotionEvent.ACTION_CANCEL, - originEvent.buttonState, - originEvent.x, - originEvent.y - ) + GodotInputHandler.handleMotionEvent(originEvent, MotionEvent.ACTION_CANCEL) } dragInProgress = false + lastDragX = 0.0f + lastDragY = 0.0f } } @@ -235,8 +203,10 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi val y = terminusEvent.y if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress && !dragInProgress) { GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f) - } else if (!scaleInProgress){ + } else if (!scaleInProgress) { dragInProgress = true + lastDragX = terminusEvent.getX(0) + lastDragY = terminusEvent.getY(0) GodotInputHandler.handleMotionEvent(terminusEvent) } return true diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index fe971cf442..83e76e49c9 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -62,6 +62,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { private final SparseIntArray mJoystickIds = new SparseIntArray(4); private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4); + private final HashSet<Integer> mHardwareKeyboardIds = new HashSet<>(); private final GodotRenderView mRenderView; private final InputManager mInputManager; @@ -114,6 +115,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { rotaryInputAxis = axis; } + boolean hasHardwareKeyboard() { + return !mHardwareKeyboardIds.isEmpty(); + } + private boolean isKeyEventGameDevice(int source) { // Note that keyboards are often (SOURCE_KEYBOARD | SOURCE_DPAD) if (source == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD)) @@ -195,7 +200,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } public boolean onTouchEvent(final MotionEvent event) { - lastSeenToolType = event.getToolType(0); + lastSeenToolType = getEventToolType(event); this.scaleGestureDetector.onTouchEvent(event); if (this.gestureDetector.onTouchEvent(event)) { @@ -221,17 +226,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } public boolean onGenericMotionEvent(MotionEvent event) { - lastSeenToolType = event.getToolType(0); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) { - // The gesture detector has handled the event. - return true; - } - - if (godotGestureHandler.onMotionEvent(event)) { - // The gesture handler has handled the event. - return true; - } + lastSeenToolType = getEventToolType(event); if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getActionMasked() == MotionEvent.ACTION_MOVE) { // Check if the device exists @@ -268,11 +263,20 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } return true; } - } else { - return handleMouseEvent(event); + return false; } - return false; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) { + // The gesture detector has handled the event. + return true; + } + + if (godotGestureHandler.onMotionEvent(event)) { + // The gesture handler has handled the event. + return true; + } + + return handleMouseEvent(event); } public void initInputDevices() { @@ -310,11 +314,17 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return; } - int sources = device.getSources(); + // Device may be an external keyboard; store the device id + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && + device.supportsSource(InputDevice.SOURCE_KEYBOARD) && + device.isExternal() && + device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) { + mHardwareKeyboardIds.add(deviceId); + } // Device may not be a joystick or gamepad - if ((sources & InputDevice.SOURCE_GAMEPAD) != InputDevice.SOURCE_GAMEPAD && - (sources & InputDevice.SOURCE_JOYSTICK) != InputDevice.SOURCE_JOYSTICK) { + if (!device.supportsSource(InputDevice.SOURCE_GAMEPAD) && + !device.supportsSource(InputDevice.SOURCE_JOYSTICK)) { return; } @@ -359,6 +369,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { @Override public void onInputDeviceRemoved(int deviceId) { + mHardwareKeyboardIds.remove(deviceId); + // Check if the device has not been already removed if (mJoystickIds.indexOfKey(deviceId) < 0) { return; @@ -440,50 +452,65 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return button; } - static boolean isMouseEvent(MotionEvent event) { - return isMouseEvent(event.getSource()); + private static int getEventToolType(MotionEvent event) { + return event.getPointerCount() > 0 ? event.getToolType(0) : MotionEvent.TOOL_TYPE_UNKNOWN; } - private static boolean isMouseEvent(int eventSource) { - boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - mouseSource = mouseSource || ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE); + static boolean isMouseEvent(MotionEvent event) { + int toolType = getEventToolType(event); + int eventSource = event.getSource(); + + switch (toolType) { + case MotionEvent.TOOL_TYPE_FINGER: + return false; + + case MotionEvent.TOOL_TYPE_MOUSE: + case MotionEvent.TOOL_TYPE_STYLUS: + case MotionEvent.TOOL_TYPE_ERASER: + return true; + + case MotionEvent.TOOL_TYPE_UNKNOWN: + default: + boolean mouseSource = + ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || + ((eventSource & (InputDevice.SOURCE_TOUCHSCREEN | InputDevice.SOURCE_STYLUS)) == InputDevice.SOURCE_STYLUS); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + mouseSource = mouseSource || + ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE); + } + return mouseSource; } - return mouseSource; } static boolean handleMotionEvent(final MotionEvent event) { - if (isMouseEvent(event)) { - return handleMouseEvent(event); - } - - return handleTouchEvent(event); + return handleMotionEvent(event, event.getActionMasked()); } - static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y) { - return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, false); + static boolean handleMotionEvent(final MotionEvent event, int eventActionOverride) { + return handleMotionEvent(event, eventActionOverride, false); } - static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, boolean doubleTap) { - return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, 0, 0, doubleTap); + static boolean handleMotionEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) { + if (isMouseEvent(event)) { + return handleMouseEvent(event, eventActionOverride, doubleTap); + } + return handleTouchEvent(event, eventActionOverride, doubleTap); } - static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleTap) { - if (isMouseEvent(eventSource)) { - return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleTap, false); - } + private static float getEventTiltX(MotionEvent event) { + // Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise. + final float orientation = event.getOrientation(); - return handleTouchEvent(eventAction, x, y, doubleTap); - } + // Tilt is zero is perpendicular to the screen and pi/2 is flat on the surface. + final float tilt = event.getAxisValue(MotionEvent.AXIS_TILT); - static boolean handleMouseEvent(final MotionEvent event) { - final int eventAction = event.getActionMasked(); - final float x = event.getX(); - final float y = event.getY(); - final int buttonsMask = event.getButtonState(); + float tiltMult = (float)Math.sin(tilt); - final float pressure = event.getPressure(); + // To be consistent with expected tilt. + return (float)-Math.sin(orientation) * tiltMult; + } + private static float getEventTiltY(MotionEvent event) { // Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise. final float orientation = event.getOrientation(); @@ -493,8 +520,26 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { float tiltMult = (float)Math.sin(tilt); // To be consistent with expected tilt. - final float tiltX = (float)-Math.sin(orientation) * tiltMult; - final float tiltY = (float)Math.cos(orientation) * tiltMult; + return (float)Math.cos(orientation) * tiltMult; + } + + static boolean handleMouseEvent(final MotionEvent event) { + return handleMouseEvent(event, event.getActionMasked()); + } + + static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride) { + return handleMouseEvent(event, eventActionOverride, false); + } + + static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) { + return handleMouseEvent(event, eventActionOverride, event.getButtonState(), doubleTap); + } + + static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, int buttonMaskOverride, boolean doubleTap) { + final float x = event.getX(); + final float y = event.getY(); + + final float pressure = event.getPressure(); float verticalFactor = 0; float horizontalFactor = 0; @@ -516,15 +561,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { sourceMouseRelative = event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE); } - return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false, sourceMouseRelative, pressure, tiltX, tiltY); - } - - static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y) { - return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, false, false); + return handleMouseEvent(eventActionOverride, buttonMaskOverride, x, y, horizontalFactor, verticalFactor, doubleTap, sourceMouseRelative, pressure, getEventTiltX(event), getEventTiltY(event)); } - static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) { - return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, 1, 0, 0); + static boolean handleMouseEvent(int eventAction, boolean sourceMouseRelative) { + return handleMouseEvent(eventAction, 0, 0f, 0f, 0f, 0f, false, sourceMouseRelative, 1f, 0f, 0f); } static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) { @@ -563,37 +604,39 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } static boolean handleTouchEvent(final MotionEvent event) { + return handleTouchEvent(event, event.getActionMasked()); + } + + static boolean handleTouchEvent(final MotionEvent event, int eventActionOverride) { + return handleTouchEvent(event, eventActionOverride, false); + } + + static boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) { final int pointerCount = event.getPointerCount(); if (pointerCount == 0) { return true; } - final float[] positions = new float[pointerCount * 3]; // pointerId1, x1, y1, pointerId2, etc... + final float[] positions = new float[pointerCount * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc... for (int i = 0; i < pointerCount; i++) { - positions[i * 3 + 0] = event.getPointerId(i); - positions[i * 3 + 1] = event.getX(i); - positions[i * 3 + 2] = event.getY(i); + positions[i * 6 + 0] = event.getPointerId(i); + positions[i * 6 + 1] = event.getX(i); + positions[i * 6 + 2] = event.getY(i); + positions[i * 6 + 3] = event.getPressure(i); + positions[i * 6 + 4] = getEventTiltX(event); + positions[i * 6 + 5] = getEventTiltY(event); } - final int action = event.getActionMasked(); final int actionPointerId = event.getPointerId(event.getActionIndex()); - return handleTouchEvent(action, actionPointerId, pointerCount, positions, false); - } - - static boolean handleTouchEvent(int eventAction, float x, float y, boolean doubleTap) { - return handleTouchEvent(eventAction, 0, 1, new float[] { 0, x, y }, doubleTap); - } - - static boolean handleTouchEvent(int eventAction, int actionPointerId, int pointerCount, float[] positions, boolean doubleTap) { - switch (eventAction) { + switch (eventActionOverride) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_DOWN: { - GodotLib.dispatchTouchEvent(eventAction, actionPointerId, pointerCount, positions, doubleTap); + GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap); return true; } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt index 0f447f0b05..11cf7b3566 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt @@ -36,7 +36,9 @@ import android.util.Log import org.godotengine.godot.io.StorageScope import java.io.IOException import java.nio.ByteBuffer +import java.nio.channels.ClosedChannelException import java.nio.channels.FileChannel +import java.nio.channels.NonWritableChannelException import kotlin.math.max /** @@ -135,6 +137,21 @@ internal abstract class DataAccess(private val filePath: String) { seek(positionFromBeginning) } + fun resize(length: Long): Int { + return try { + fileChannel.truncate(length) + FileErrors.OK.nativeValue + } catch (e: NonWritableChannelException) { + FileErrors.FILE_CANT_OPEN.nativeValue + } catch (e: ClosedChannelException) { + FileErrors.FILE_CANT_OPEN.nativeValue + } catch (e: IllegalArgumentException) { + FileErrors.INVALID_PARAMETER.nativeValue + } catch (e: IOException) { + FileErrors.FAILED.nativeValue + } + } + fun position(): Long { return try { fileChannel.position() diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt index 50741c1aab..1d773467e8 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt @@ -45,7 +45,6 @@ class FileAccessHandler(val context: Context) { companion object { private val TAG = FileAccessHandler::class.java.simpleName - private const val FILE_NOT_FOUND_ERROR_ID = -1 internal const val INVALID_FILE_ID = 0 private const val STARTING_FILE_ID = 1 @@ -118,7 +117,7 @@ class FileAccessHandler(val context: Context) { lastFileId } ?: INVALID_FILE_ID } catch (e: FileNotFoundException) { - FILE_NOT_FOUND_ERROR_ID + FileErrors.FILE_NOT_FOUND.nativeValue } catch (e: Exception) { Log.w(TAG, "Error while opening $path", e) INVALID_FILE_ID @@ -190,6 +189,14 @@ class FileAccessHandler(val context: Context) { } } + fun fileResize(fileId: Int, length: Long): Int { + if (!hasFileId(fileId)) { + return FileErrors.FAILED.nativeValue + } + + return files[fileId].resize(length) + } + fun fileGetPosition(fileId: Int): Long { if (!hasFileId(fileId)) { return 0L diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileErrors.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileErrors.kt new file mode 100644 index 0000000000..2df0195de7 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileErrors.kt @@ -0,0 +1,53 @@ +/**************************************************************************/ +/* FileErrors.kt */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +package org.godotengine.godot.io.file + +/** + * Set of errors that may occur when performing data access. + */ +internal enum class FileErrors(val nativeValue: Int) { + OK(0), + FAILED(-1), + FILE_NOT_FOUND(-2), + FILE_CANT_OPEN(-3), + INVALID_PARAMETER(-4); + + companion object { + fun fromNativeError(error: Int): FileErrors? { + for (fileError in entries) { + if (fileError.nativeValue == error) { + return fileError + } + } + return null + } + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java index c0912ca4dc..c975c29e96 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java @@ -112,19 +112,18 @@ public abstract class GodotPlugin { /** * Register the plugin with Godot native code. * <p> - * This method is invoked by the Godot Engine on the render thread. + * This method is invoked on the render thread to register the plugin on engine startup. */ public final void onRegisterPluginWithGodotNative() { - registeredSignals.putAll( - registerPluginWithGodotNative(this, getPluginName(), getPluginMethods(), getPluginSignals())); - } + final String pluginName = getPluginName(); + if (!nativeRegisterSingleton(pluginName, this)) { + return; + } - private static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject, - String pluginName, List<String> pluginMethods, Set<SignalInfo> pluginSignals) { - nativeRegisterSingleton(pluginName, pluginObject); + List<String> pluginMethods = getPluginMethods(); Set<Method> filteredMethods = new HashSet<>(); - Class<?> clazz = pluginObject.getClass(); + Class<?> clazz = getClass(); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { @@ -156,15 +155,14 @@ public abstract class GodotPlugin { nativeRegisterMethod(pluginName, method.getName(), method.getReturnType().getName(), pt); } + Set<SignalInfo> pluginSignals = getPluginSignals(); + // Register the signals for this plugin. - Map<String, SignalInfo> registeredSignals = new HashMap<>(); for (SignalInfo signalInfo : pluginSignals) { String signalName = signalInfo.getName(); nativeRegisterSignal(pluginName, signalName, signalInfo.getParamTypesNames()); registeredSignals.put(signalName, signalInfo); } - - return registeredSignals; } /** @@ -408,7 +406,7 @@ public abstract class GodotPlugin { * Used to setup a {@link GodotPlugin} instance. * @param p_name Name of the instance. */ - private static native void nativeRegisterSingleton(String p_name, Object object); + private static native boolean nativeRegisterSingleton(String p_name, Object object); /** * Used to complete registration of the {@link GodotPlugin} instance's methods. diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java index 9df890e6bd..4e8e82a70a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java @@ -125,7 +125,7 @@ public final class PermissionsUtil { } activity.requestPermissions(requestedPermissions.toArray(new String[0]), REQUEST_ALL_PERMISSION_REQ_CODE); - return true; + return false; } /** @@ -281,8 +281,9 @@ public final class PermissionsUtil { public static boolean hasManifestPermission(Context context, String permission) { try { for (String p : getManifestPermissions(context)) { - if (permission.equals(p)) + if (permission.equals(p)) { return true; + } } } catch (PackageManager.NameNotFoundException ignored) { } @@ -299,8 +300,9 @@ public final class PermissionsUtil { public static ArrayList<String> getManifestPermissions(Context context) throws PackageManager.NameNotFoundException { PackageManager packageManager = context.getPackageManager(); PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS); - if (packageInfo.requestedPermissions == null) - return new ArrayList<String>(); + if (packageInfo.requestedPermissions == null) { + return new ArrayList<>(); + } return new ArrayList<>(Arrays.asList(packageInfo.requestedPermissions)); } |
