diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java
index 5c3d303ab5..fc05da90d6 100644
--- a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java
@@ -40,7 +40,9 @@
import android.view.ScaleGestureDetector;
import android.view.View;
import com.jme3.input.JoyInput;
+import com.jme3.input.MouseInput;
import com.jme3.input.TouchInput;
+import com.jme3.input.dummy.DummyMouseInput;
import com.jme3.system.AppSettings;
import java.util.logging.Logger;
@@ -60,11 +62,12 @@ public class AndroidInputHandler implements View.OnTouchListener,
protected GLSurfaceView view;
protected AndroidTouchInput touchInput;
protected AndroidJoyInput joyInput;
-
+ protected MouseInput mouseInput;
public AndroidInputHandler() {
touchInput = new AndroidTouchInput(this);
joyInput = new AndroidJoyInput(this);
+ mouseInput = new DummyMouseInput();
}
public void setView(View view) {
@@ -118,6 +121,10 @@ public JoyInput getJoyInput() {
return joyInput;
}
+ public MouseInput getMouseInput() {
+ return mouseInput;
+ }
+
/*
* Android input events include the source from which the input came from.
* We must look at the source of the input event to determine which type
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java
index 5ee0f757b2..7274a857ad 100644
--- a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java
@@ -37,6 +37,9 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+
+import com.jme3.system.AppSettings;
+
import java.util.logging.Logger;
/**
@@ -55,6 +58,7 @@ public class AndroidInputHandler14 extends AndroidInputHandler implements View.O
public AndroidInputHandler14() {
touchInput = new AndroidTouchInput14(this);
joyInput = new AndroidJoyInput14(this);
+ mouseInput = new AndroidMouseInput14(this);
}
@Override
@@ -71,6 +75,12 @@ protected void addListeners(GLSurfaceView view) {
view.setOnGenericMotionListener(this);
}
+ @Override
+ public void loadSettings(AppSettings settings) {
+ super.loadSettings(settings);
+ ((AndroidMouseInput14)mouseInput).loadSettings(settings);
+ }
+
@Override
public boolean onHover(View view, MotionEvent event) {
if (view != getView()) {
@@ -91,6 +101,12 @@ public boolean onHover(View view, MotionEvent event) {
consumed = ((AndroidTouchInput14)touchInput).onHover(event);
}
+ boolean isMouse = ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE);
+ if (isMouse && mouseInput != null) {
+ // send the event to the mouse processor
+ consumed = ((AndroidMouseInput14)mouseInput).onHover(event);
+ }
+
return consumed;
}
@@ -116,6 +132,28 @@ public boolean onGenericMotion(View view, MotionEvent event) {
consumed = consumed || ((AndroidJoyInput14)joyInput).onGenericMotion(event);
}
+ if((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
+ consumed = consumed || ((AndroidMouseInput14)mouseInput).onGenericMotion(event);
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ if (view != getView()) {
+ return false;
+ }
+
+ boolean consumed = super.onTouch(view, event);
+
+ // Mouse movement while button down is received as onTouch event instead
+ boolean isMouse = ((event.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE);
+ if (isMouse && mouseInput != null) {
+ // send the event to the mouse processor
+ consumed |= ((AndroidMouseInput14)mouseInput).onGenericMotion(event);
+ }
+
return consumed;
}
@@ -154,7 +192,6 @@ public boolean onKey(View view, int keyCode, KeyEvent event) {
}
return consumed;
-
}
}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler24.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler24.java
new file mode 100644
index 0000000000..ce405ec3f4
--- /dev/null
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler24.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2026 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.android;
+
+/**
+ * AndroidInputHandler24 extends AndroidInputHandler14 to
+ * use AndroidMouseInput24 which adds usage of newer events and also enables cursor visibility
+ * and cursor image change.
+ *
+ * @author joliver82
+ */
+public class AndroidInputHandler24 extends AndroidInputHandler14 {
+
+ public AndroidInputHandler24() {
+ super();
+ mouseInput = new AndroidMouseInput24(this);
+ }
+
+}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler26.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler26.java
new file mode 100644
index 0000000000..f5b9c5e875
--- /dev/null
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler26.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2009-2026 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.android;
+
+import android.opengl.GLSurfaceView;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * AndroidInputHandler26 extends AndroidInputHandler24 to
+ * add the onCapturedPointer events that where added in Android rev 26.
+ * The onCapturedPointer events are received when mouse is grabbed/captured.
+ *
+ * @author joliver82
+ */
+public class AndroidInputHandler26 extends AndroidInputHandler24 implements View.OnCapturedPointerListener {
+
+ public AndroidInputHandler26() {
+ super();
+ mouseInput = new AndroidMouseInput26(this);
+ }
+
+ protected void removeListeners(GLSurfaceView view) {
+ super.removeListeners(view);
+ view.setOnCapturedPointerListener(null);
+ }
+
+ @Override
+ protected void addListeners(GLSurfaceView view) {
+ super.addListeners(view);
+ view.setOnCapturedPointerListener(this);
+ }
+
+ @Override
+ public boolean onCapturedPointer(View view, MotionEvent event) {
+ if (view != getView()) {
+ return false;
+ }
+
+ boolean consumed = false;
+ boolean isMouse = ((event.getSource() & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE);
+ if (isMouse && mouseInput != null) {
+ consumed = ((AndroidMouseInput26)mouseInput).onCapturedPointer(event);
+ }
+
+ return consumed;
+ }
+}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java
new file mode 100644
index 0000000000..e93f69ac95
--- /dev/null
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2009-2026 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.android;
+
+import android.view.MotionEvent;
+
+import com.jme3.cursors.plugins.JmeCursor;
+import com.jme3.input.MouseInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.InputEvent;
+import com.jme3.input.event.MouseButtonEvent;
+import com.jme3.input.event.MouseMotionEvent;
+import com.jme3.system.AppSettings;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * AndroidMouseInput14 implements MouseInput to add mouse support for jME3
+ * uses the onGenericMotion events that where added in Android rev 12 and MotionEvent.getButtonState
+ * from Android rev 14 so added "14" suffix to the class to specify the Android required rev and
+ * match other classes naming
+ *
+ * @author joliver82
+ */
+public class AndroidMouseInput14 implements MouseInput {
+ private static final Logger logger = Logger.getLogger(AndroidMouseInput14.class.getName());
+
+ protected AndroidInputHandler inputHandler;
+
+ private boolean initialized = false;
+ private RawInputListener listener = null;
+ private ConcurrentLinkedQueue eventQueue = new ConcurrentLinkedQueue<>();
+ private float scaleX = 1f;
+ private float scaleY = 1f;
+
+ protected class MouseState {
+ int x, y, wheel;
+ boolean left, right, center;
+
+ protected void setStartPosition(int startingX,int startingY) {
+ x = startingX;
+ y = startingY;
+ }
+
+ protected int updateX(int newX) {
+ int deltaX=newX-x;
+ x=newX;
+ return deltaX;
+ }
+
+ protected int incrementX(int deltaX) {
+ x+=deltaX;
+ return x;
+ }
+
+ protected int updateY(int newY) {
+ int deltaY=newY-y;
+ y=newY;
+ return deltaY;
+ }
+
+ protected int incrementY(int deltaY) {
+ y+=deltaY;
+ return y;
+ }
+
+ protected int incrementWheel(int deltaWheel) {
+ wheel+=deltaWheel;
+ return wheel;
+ }
+
+ protected boolean updateLeftButton(boolean left) {
+ if(this.left == left) {
+ return false;
+ }
+ this.left = left;
+ return true;
+ }
+
+ protected boolean updateRightButton(boolean right) {
+ if(this.right == right) {
+ return false;
+ }
+ this.right = right;
+ return true;
+ }
+
+ protected boolean updateCenterButton(boolean center) {
+ if(this.center == center) {
+ return false;
+ }
+ this.center = center;
+ return true;
+ }
+ }
+
+ MouseState currentMouseState = new MouseState();
+
+ public AndroidMouseInput14(AndroidInputHandler inputHandler) {
+ this.inputHandler = inputHandler;
+ }
+
+ protected int getJmeX(float origX) {
+ return (int) (origX * scaleX);
+ }
+
+ protected int getJmeY(float origY) {
+ return (int) (origY * scaleY);
+ }
+
+ public void loadSettings(AppSettings settings) {
+ // view width and height are 0 until the view is displayed on the screen
+ if (inputHandler.getView().getWidth() != 0 && inputHandler.getView().getHeight() != 0) {
+ scaleX = settings.getWidth() / (float)inputHandler.getView().getWidth();
+ scaleY = settings.getHeight() / (float)inputHandler.getView().getHeight();
+ currentMouseState.setStartPosition(inputHandler.getView().getWidth()/2, inputHandler.getView().getHeight()/2);
+ }
+
+ if (logger.isLoggable(Level.FINE)) {
+ logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}",
+ new Object[]{scaleX, scaleY});
+ }
+
+ }
+
+ protected void addMouseMotionEventFixedPositions(int x, int y, int deltaWheel) {
+ int deltaX=currentMouseState.updateX(x);
+ int deltaY=currentMouseState.updateY(y);
+ int wheel=currentMouseState.incrementWheel(deltaWheel);
+
+ logger.log(Level.INFO, "Mouse motion event: " + x + "x" + y + " wheel: " + wheel);
+
+ eventQueue.add(new MouseMotionEvent(x, y, deltaX, deltaY, wheel, deltaWheel));
+ }
+
+ protected void addMouseMotionEventRelativePositions(int deltaX, int deltaY, int deltaWheel) {
+ int x=currentMouseState.incrementX(deltaX);
+ int y=currentMouseState.incrementY(deltaY);
+ int wheel=currentMouseState.incrementWheel(deltaWheel);
+
+ logger.log(Level.INFO, "Mouse motion event: " + x + "x" + y + " wheel: " + wheel);
+
+ eventQueue.add(new MouseMotionEvent(x, y, deltaX, deltaY, wheel, deltaWheel));
+ }
+
+ protected boolean addMouseButtonEvent(boolean left, boolean right, boolean center, int x, int y) {
+ boolean eventAdded = false;
+ if(currentMouseState.updateLeftButton(left)) {
+ eventQueue.add(new MouseButtonEvent(MouseInput.BUTTON_LEFT, left, x, y));
+ logger.log(Level.INFO, "Mouse button left: " + left);
+ eventAdded = true;
+ }
+ if(currentMouseState.updateRightButton(right)) {
+ eventQueue.add(new MouseButtonEvent(MouseInput.BUTTON_RIGHT, right, x, y));
+ logger.log(Level.INFO, "Mouse button right: " + right);
+ eventAdded = true;
+ }
+ if(currentMouseState.updateCenterButton(center)) {
+ eventQueue.add(new MouseButtonEvent(MouseInput.BUTTON_MIDDLE, center, x, y));
+ logger.log(Level.INFO, "Mouse button center: " + center);
+ eventAdded = true;
+ }
+ return eventAdded;
+ }
+
+ public boolean onHover(MotionEvent event) {
+ boolean consumed = false;
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_SCROLL:
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ case MotionEvent.ACTION_HOVER_EXIT:
+ case MotionEvent.ACTION_HOVER_ENTER:
+ addMouseMotionEventFixedPositions(getJmeX(event.getX()), getJmeY(event.getY()), (int) event.getAxisValue(MotionEvent.AXIS_VSCROLL));
+ consumed = true;
+ break;
+ }
+
+ return consumed;
+ }
+
+ public boolean onGenericMotion(MotionEvent event) {
+ boolean consumed = false;
+ boolean btnEventReceived = false;
+ boolean leftPressed = false, rightPressed = false, centerPressed = false;
+
+ int btnState = event.getButtonState();
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ if((btnState & MotionEvent.BUTTON_PRIMARY) == MotionEvent.BUTTON_PRIMARY) {
+ leftPressed = true;
+ }
+ if((btnState & MotionEvent.BUTTON_SECONDARY) == MotionEvent.BUTTON_SECONDARY) {
+ rightPressed = true;
+ }
+ if((btnState & MotionEvent.BUTTON_TERTIARY) == MotionEvent.BUTTON_TERTIARY) {
+ centerPressed = true;
+ }
+ btnEventReceived = true;
+ break;
+
+ case MotionEvent.ACTION_UP:
+ if((btnState & MotionEvent.BUTTON_PRIMARY) == MotionEvent.BUTTON_PRIMARY) {
+ leftPressed = false;
+ }
+ if((btnState & MotionEvent.BUTTON_SECONDARY) == MotionEvent.BUTTON_SECONDARY) {
+ rightPressed = false;
+ }
+ if((btnState & MotionEvent.BUTTON_TERTIARY) == MotionEvent.BUTTON_TERTIARY) {
+ centerPressed = false;
+ }
+ btnEventReceived = true;
+ break;
+
+ case MotionEvent.ACTION_HOVER_EXIT:
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_SCROLL:
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ addMouseMotionEventFixedPositions(getJmeX(event.getX()), getJmeY(event.getY()), (int) event.getAxisValue(MotionEvent.AXIS_VSCROLL));
+ consumed = true;
+ break;
+ }
+
+ if (btnEventReceived) {
+ consumed = addMouseButtonEvent(leftPressed, rightPressed, centerPressed, getJmeX(event.getX()), getJmeY(event.getY()));
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public void setCursorVisible(boolean visible) {
+ logger.log(Level.FINE, "Cannot hide mouse till API 24");
+ }
+
+
+ @Override
+ public int getButtonCount() {
+ return 3; // No way to get the number of buttons, defaulting to 3 buttons
+ }
+
+ @Override
+ public void setNativeCursor(JmeCursor cursor) {
+ logger.log(Level.FINE, "Cannot change cursor till API 24");
+ }
+
+ @Override
+ public void initialize() {
+ initialized = true;
+ }
+
+ @Override
+ public void update() {
+ if (listener != null) {
+ InputEvent inputEvent;
+
+ while ((inputEvent = eventQueue.poll()) != null) {
+ if (inputEvent instanceof MouseMotionEvent) {
+ listener.onMouseMotionEvent((MouseMotionEvent)inputEvent);
+ } else if (inputEvent instanceof MouseButtonEvent) {
+ listener.onMouseButtonEvent((MouseButtonEvent)inputEvent);
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public void destroy() {
+ initialized = false;
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ @Override
+ public void setInputListener(RawInputListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public long getInputTimeNanos() {
+ return System.nanoTime();
+ }
+}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java
new file mode 100644
index 0000000000..bdc1c5fef7
--- /dev/null
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2009-2026 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.android;
+
+import android.graphics.Bitmap;
+import android.view.MotionEvent;
+import android.view.PointerIcon;
+
+import com.jme3.cursors.plugins.JmeCursor;
+
+import java.nio.IntBuffer;
+import java.util.logging.Logger;
+
+/**
+ * AndroidMouseInput24 extends AndroidMouseInput14 to improve mouse support
+ * using new events defined in API rev 24 and adding support for cursor change and cursor visibility
+ *
+ * @author joliver82
+ */
+public class AndroidMouseInput24 extends AndroidMouseInput14{
+ private static final Logger logger = Logger.getLogger(AndroidMouseInput24.class.getName());
+
+ public AndroidMouseInput24(AndroidInputHandler inputHandler) {
+ super(inputHandler);
+ }
+
+ @Override
+ public boolean onGenericMotion(MotionEvent event) {
+
+ boolean consumed = super.onGenericMotion(event);
+
+ if (!consumed) {
+ boolean leftPressed = false, rightPressed = false, centerPressed = false;
+ boolean btnEventReceived = false;
+ int btnAction = event.getActionButton();
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_BUTTON_PRESS:
+ if(btnAction == MotionEvent.BUTTON_PRIMARY) {
+ leftPressed = true;
+ }
+ if(btnAction == MotionEvent.BUTTON_SECONDARY) {
+ rightPressed = true;
+ }
+ if(btnAction == MotionEvent.BUTTON_TERTIARY) {
+ centerPressed = true;
+ }
+ btnEventReceived = true;
+ break;
+
+ case MotionEvent.ACTION_BUTTON_RELEASE:
+ if(btnAction == MotionEvent.BUTTON_PRIMARY) {
+ leftPressed = false;
+ }
+ if(btnAction == MotionEvent.BUTTON_SECONDARY) {
+ rightPressed = false;
+ }
+ if(btnAction == MotionEvent.BUTTON_TERTIARY) {
+ centerPressed = false;
+ }
+ btnEventReceived = true;
+ break;
+ }
+
+ if (btnEventReceived) {
+ consumed = addMouseButtonEvent(leftPressed, rightPressed, centerPressed, getJmeX(event.getX()), getJmeY(event.getY()));
+ }
+ }
+ return consumed;
+ }
+
+ @Override
+ public void setCursorVisible(boolean visible) {
+ if(inputHandler.getView()!=null) {
+ if(visible) {
+ inputHandler.getView().setPointerIcon(null);
+ } else {
+ inputHandler.getView().setPointerIcon(PointerIcon.getSystemIcon(inputHandler.getView().getContext(), PointerIcon.TYPE_NULL));
+ }
+ }
+ }
+
+ @Override
+ public void setNativeCursor(JmeCursor cursor) {
+ if(inputHandler.getView()!=null) {
+ if(cursor!=null) {
+ // Translate into Android Bitmap format ARGB888. Assuming input image as RGBA
+ int bufferSize = cursor.getHeight()*cursor.getWidth();
+ int[] outputBitmap=new int[bufferSize];
+ IntBuffer inputImage = cursor.getImagesData().asReadOnlyBuffer();
+ inputImage.clear();
+ int[] tmpPixel = new int[4];
+ for(int i=0 ; i< bufferSize; ++i) {
+ inputImage.get(tmpPixel);
+ outputBitmap[i] = (tmpPixel[3] & 0xff) << 24 | (tmpPixel[0] & 0xff) << 16 | (tmpPixel[1] & 0xff) << 8 | (tmpPixel[2] & 0xff);
+ }
+ PointerIcon pointer = PointerIcon.create(
+ Bitmap.createBitmap(outputBitmap, cursor.getWidth(), cursor.getHeight(), Bitmap.Config.ARGB_8888),
+ cursor.getXHotSpot(),
+ cursor.getYHotSpot());
+ inputHandler.getView().setPointerIcon(pointer);
+ } else {
+ inputHandler.getView().setPointerIcon(null);
+ }
+ }
+ }
+
+}
diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java
new file mode 100644
index 0000000000..34240f0104
--- /dev/null
+++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2009-2026 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.input.android;
+
+import android.graphics.Bitmap;
+import android.view.MotionEvent;
+import android.view.PointerIcon;
+
+import com.jme3.cursors.plugins.JmeCursor;
+
+import java.nio.IntBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * AndroidMouseInput26 extends AndroidMouseInput24 to improve mouse support
+ * adding grab/capture support using onCapturedPointer events.
+ *
+ * @author joliver82
+ */
+public class AndroidMouseInput26 extends AndroidMouseInput24{
+ private static final Logger logger = Logger.getLogger(AndroidMouseInput26.class.getName());
+
+ public AndroidMouseInput26(AndroidInputHandler inputHandler) {
+ super(inputHandler);
+ }
+
+ public boolean onCapturedPointer(MotionEvent event) {
+ boolean consumed = false;
+ boolean btnEventReceived = false;
+ boolean leftPressed = false, rightPressed = false, centerPressed = false;
+
+ int btnAction = event.getActionButton();
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_BUTTON_PRESS:
+ if(btnAction == MotionEvent.BUTTON_PRIMARY) {
+ leftPressed = true;
+ }
+ if(btnAction == MotionEvent.BUTTON_SECONDARY) {
+ rightPressed = true;
+ }
+ if(btnAction == MotionEvent.BUTTON_TERTIARY) {
+ centerPressed = true;
+ }
+ btnEventReceived = true;
+ break;
+
+ case MotionEvent.ACTION_BUTTON_RELEASE:
+ if(btnAction == MotionEvent.BUTTON_PRIMARY) {
+ leftPressed = false;
+ }
+ if(btnAction == MotionEvent.BUTTON_SECONDARY) {
+ rightPressed = false;
+ }
+ if(btnAction == MotionEvent.BUTTON_TERTIARY) {
+ centerPressed = false;
+ }
+ btnEventReceived = true;
+ break;
+
+ case MotionEvent.ACTION_HOVER_EXIT:
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_SCROLL:
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ addMouseMotionEventRelativePositions(getJmeX(event.getX()), getJmeY(event.getY()), (int) event.getAxisValue(MotionEvent.AXIS_VSCROLL));
+ consumed = true;
+ break;
+ }
+
+ if (btnEventReceived) {
+ consumed = addMouseButtonEvent(leftPressed, rightPressed, centerPressed, getJmeX(event.getX()), getJmeY(event.getY()));
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public void setCursorVisible(boolean visible) {
+ if(!visible) {
+ inputHandler.getView().requestPointerCapture();
+ } else {
+ inputHandler.getView().releasePointerCapture();
+ }
+ }
+
+}
diff --git a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
index 70911d855a..6d8cf0962b 100644
--- a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
+++ b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
@@ -51,9 +51,10 @@
import com.jme3.input.*;
import com.jme3.input.android.AndroidInputHandler;
import com.jme3.input.android.AndroidInputHandler14;
+import com.jme3.input.android.AndroidInputHandler24;
+import com.jme3.input.android.AndroidInputHandler26;
import com.jme3.input.controls.SoftTextDialogInputListener;
import com.jme3.input.dummy.DummyKeyInput;
-import com.jme3.input.dummy.DummyMouseInput;
import com.jme3.renderer.android.AndroidGL;
import com.jme3.renderer.opengl.*;
import com.jme3.system.*;
@@ -125,7 +126,11 @@ public GLSurfaceView createView(Context context) {
GLSurfaceView view = new GLSurfaceView(context);
logger.log(Level.INFO, "Android Build Version: {0}", Build.VERSION.SDK_INT);
if (androidInput == null) {
- if (Build.VERSION.SDK_INT >= 14) {
+ if (Build.VERSION.SDK_INT >= 26) {
+ androidInput = new AndroidInputHandler26();
+ } else if (Build.VERSION.SDK_INT >= 24) {
+ androidInput = new AndroidInputHandler24();
+ } else if (Build.VERSION.SDK_INT >= 14) {
androidInput = new AndroidInputHandler14();
} else if (Build.VERSION.SDK_INT >= 9) {
androidInput = new AndroidInputHandler();
@@ -141,6 +146,9 @@ public GLSurfaceView createView(Context context) {
view.setFocusableInTouchMode(true);
view.setFocusable(true);
+ view.setFocusedByDefault(true);
+ view.requestFocus();
+ //view.setClickable(true);
// setFormat must be set before AndroidConfigChooser is called by the surfaceview.
// if setFormat is called after ConfigChooser is called, then execution
@@ -310,7 +318,7 @@ public com.jme3.renderer.Renderer getRenderer() {
@Override
public MouseInput getMouseInput() {
- return new DummyMouseInput();
+ return androidInput.getMouseInput();
}
@Override