Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/j3o-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '17'
java-version: '25'

- name: Scan J3O assets
run: ./gradlew :jme3-desktop:scanJ3O --console=plain
86 changes: 20 additions & 66 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'
java-version: '25'
- name: Validate the Gradle wrapper
uses: gradle/actions/wrapper-validation@v6.1.0
- name: Run Checkstyle
Expand All @@ -96,7 +96,7 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'
java-version: '25'
- name: Validate the Gradle wrapper
uses: gradle/actions/wrapper-validation@v6.1.0
- name: Run SpotBugs
Expand All @@ -120,6 +120,11 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v6
- name: Setup the java environment
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '25'
- name: Start xvfb
run: |
Xvfb :99 -ac -screen 0 1024x768x16 &
Expand Down Expand Up @@ -150,45 +155,6 @@ jobs:
**/build/changed-images/**
**/build/test-results/**

# Build iOS natives
BuildIosNatives:
name: Build natives for iOS
runs-on: macOS-14

steps:
- name: Check default JAVAs
run: echo $JAVA_HOME --- $JAVA_HOME_8_X64 --- $JAVA_HOME_11_X64 --- $JAVA_HOME_17_X64 --- $JAVA_HOME_21_X64 ---

- name: Setup the java environment
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'

- name: Setup the XCode version to 15.1.0
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0
with:
xcode-version: '15.1.0'

- name: Clone the repo
uses: actions/checkout@v6
with:
fetch-depth: 1

- name: Validate the Gradle wrapper
uses: gradle/actions/wrapper-validation@v6.1.0

- name: Build
run: |
./gradlew -PuseCommitHashAsVersionName=true --no-daemon -PbuildNativeProjects=true \
:jme3-ios-native:build

- name: Upload natives
uses: actions/upload-artifact@v7.0.1
with:
name: ios-natives
path: jme3-ios-native/template/META-INF/robovm/ios/libs/jme3-ios-native.xcframework

# Build the natives on android
BuildAndroidNatives:
name: Build natives for android
Expand All @@ -202,11 +168,11 @@ jobs:
with:
fetch-depth: 1

- name: Setup Java 17
- name: Setup Java 25
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '17'
java-version: '25'

- name: Check java version
run: java -version
Expand Down Expand Up @@ -234,7 +200,7 @@ jobs:

# Build the engine, we only deploy from ubuntu-latest jdk25
BuildJMonkey:
needs: [BuildAndroidNatives, BuildIosNatives]
needs: [BuildAndroidNatives]
name: Build on ${{ matrix.osName }} jdk${{ matrix.jdk }}
runs-on: ${{ matrix.os }}
strategy:
Expand All @@ -259,18 +225,18 @@ jobs:
with:
fetch-depth: 1

- name: Setup the java environment
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: ${{ matrix.jdk }}

- name: Download natives for android
uses: actions/download-artifact@v8.0.1
with:
name: android-natives
path: build/native

- name: Download natives for iOS
uses: actions/download-artifact@v8.0.1
with:
name: ios-natives
path: jme3-ios-native/template/META-INF/robovm/ios/libs/jme3-ios-native.xcframework

- name: Validate the Gradle wrapper
uses: gradle/actions/wrapper-validation@v6.1.0
- name: Build Engine
Expand Down Expand Up @@ -453,25 +419,19 @@ jobs:
with:
fetch-depth: 1

# Setup jdk 21 used for building Maven-style artifacts
# Setup jdk 25 used for building Maven-style artifacts
- name: Setup the java environment
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
with:
distribution: 'temurin'
java-version: '21'
java-version: '25'

- name: Download natives for android
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: android-natives
path: build/native

- name: Download natives for iOS
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ios-natives
path: jme3-ios-native/template/META-INF/robovm/ios/libs/jme3-ios-native.xcframework

- name: Rebuild the maven artifacts and upload them to Sonatype's maven-snapshots repo
run: |
if [ "${{ secrets.CENTRAL_PASSWORD }}" = "" ];
Expand Down Expand Up @@ -502,12 +462,12 @@ jobs:
with:
fetch-depth: 1

# Setup jdk 21 used for building Sonatype artifacts
# Setup jdk 25 used for building Sonatype artifacts
- name: Setup the java environment
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'
java-version: '25'

# Download all the stuff...
- name: Download maven artifacts
Expand All @@ -528,12 +488,6 @@ jobs:
name: android-natives
path: build/native

- name: Download natives for iOS
uses: actions/download-artifact@v8.0.1
with:
name: ios-natives
path: jme3-ios-native/template/META-INF/robovm/ios/libs/jme3-ios-native.xcframework

- name: Rebuild the maven artifacts and upload them to Sonatype's Central Publisher Portal
run: |
if [ "${{ secrets.CENTRAL_PASSWORD }}" = "" ];
Expand Down
8 changes: 8 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@ apply from: file('version.gradle')

allprojects {
repositories {
mavenLocal()
mavenCentral()
maven {
name = 'CentralSnapshots'
url = uri('https://central.sonatype.com/repository/maven-snapshots/')
mavenContent {
snapshotsOnly()
}
}
google()
}
tasks.withType(Jar) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ protected void addListeners(GLSurfaceView view) {

public void loadSettings(AppSettings settings) {
touchInput.loadSettings(settings);
joyInput.loadSettings(settings);
}

public TouchInput getTouchInput() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@
*/
package com.jme3.input.android;

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Vibrator;
import com.jme3.input.InputManager;
import com.jme3.input.JoyInput;
import com.jme3.input.Joystick;
Expand All @@ -42,6 +40,7 @@
import com.jme3.input.event.JoyAxisEvent;
import com.jme3.input.event.JoyButtonEvent;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeSystem;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
Expand All @@ -55,9 +54,9 @@
* This class manages all the joysticks and feeds the inputs from each back
* to jME's InputManager.
*
* This handler also supports the joystick.rumble(rumbleAmount) method. In this
* case, when joystick.rumble(rumbleAmount) is called, the Android device will vibrate
* if the device has a built-in vibrate motor.
* This handler also supports redirecting joystick.rumble(rumbleAmount) to the
* Android device vibrator if AppSettings#setOnDeviceJoystickRumble(boolean) is
* enabled and the device has a built-in vibrate motor.
*
* Because Android does not allow for the user to define the intensity of the
* vibration, the rumble amount (ie strength) is converted into vibration pulses
Expand Down Expand Up @@ -92,33 +91,21 @@ public class AndroidJoyInput implements JoyInput {
private RawInputListener listener = null;
private ConcurrentLinkedQueue<InputEvent> eventQueue = new ConcurrentLinkedQueue<>();
private AndroidSensorJoyInput sensorJoyInput;
private Vibrator vibrator = null;
private boolean vibratorActive = false;
private long maxRumbleTime = 250; // 250ms
private boolean onDeviceJoystickRumble = false;

public AndroidJoyInput(AndroidInputHandler inputHandler) {
this.inputHandler = inputHandler;
sensorJoyInput = new AndroidSensorJoyInput(this);
}

public void setView(GLSurfaceView view) {
if (view == null) {
vibrator = null;
} else {
// Get instance of Vibrator from current Context
vibrator = (Vibrator) view.getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator == null) {
logger.log(Level.FINE, "Vibrator Service not found.");
}
}

if (sensorJoyInput != null) {
sensorJoyInput.setView(view);
}
}

public void loadSettings(AppSettings settings) {

onDeviceJoystickRumble = settings.isOnDeviceJoystickRumble();
}

public void addEvent(InputEvent event) {
Expand All @@ -133,8 +120,8 @@ public void pauseJoysticks() {
if (sensorJoyInput != null) {
sensorJoyInput.pauseSensors();
}
if (vibrator != null && vibratorActive) {
vibrator.cancel();
if (onDeviceJoystickRumble) {
JmeSystem.setDeviceRumble(0f);
}

}
Expand Down Expand Up @@ -183,27 +170,8 @@ public long getInputTimeNanos() {

@Override
public void setJoyRumble(int joyId, float amount) {
// convert amount to pulses since Android doesn't allow intensity
if (vibrator != null) {
final long rumbleOnDur = (long)(amount * maxRumbleTime); // ms to pulse vibration on
final long rumbleOffDur = maxRumbleTime - rumbleOnDur; // ms to delay between pulses
final long[] rumblePattern = {
0, // start immediately
rumbleOnDur, // time to leave vibration on
rumbleOffDur // time to delay between vibrations
};
final int rumbleRepeatFrom = 0; // index into rumble pattern to repeat from

// logger.log(Level.FINE, "Rumble amount: {0}, rumbleOnDur: {1}, rumbleOffDur: {2}",
// new Object[]{amount, rumbleOnDur, rumbleOffDur});

if (rumbleOnDur > 0) {
vibrator.vibrate(rumblePattern, rumbleRepeatFrom);
vibratorActive = true;
} else {
vibrator.cancel();
vibratorActive = false;
}
if (onDeviceJoystickRumble && JmeSystem.isDeviceRumbleSupported()) {
JmeSystem.setDeviceRumble(amount);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Environment;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import com.jme3.audio.AudioRenderer;
Expand Down Expand Up @@ -33,6 +35,7 @@ public class JmeAndroidSystem extends JmeSystemDelegate {

private static View view;
private static String audioRendererType = AppSettings.ANDROID_OPENAL_SOFT;
private static final long DEVICE_RUMBLE_TIME = 250;

static {
try {
Expand Down Expand Up @@ -210,6 +213,51 @@ public static String getAudioRendererType() {
return audioRendererType;
}

@Override
public boolean isDeviceRumbleSupported() {
Vibrator vibrator = getVibrator();
return vibrator != null && vibrator.hasVibrator();
}

@Override
@SuppressWarnings("deprecation")
public void setDeviceRumble(float amount) {
Vibrator vibrator = getVibrator();
if (vibrator == null || !vibrator.hasVibrator()) {
return;
}

float rumble = Math.max(0f, Math.min(1f, amount));
try {
if (rumble <= 0f) {
vibrator.cancel();
return;
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && vibrator.hasAmplitudeControl()) {
int amplitude = Math.max(1, Math.round(rumble * 255f));
vibrator.vibrate(VibrationEffect.createWaveform(
new long[]{0, DEVICE_RUMBLE_TIME},
new int[]{0, amplitude},
0));
} else {
long rumbleOnDur = (long) (rumble * DEVICE_RUMBLE_TIME);
long rumbleOffDur = DEVICE_RUMBLE_TIME - rumbleOnDur;
vibrator.vibrate(new long[]{0, rumbleOnDur, rumbleOffDur}, 0);
}
} catch (SecurityException ignored) {
// Applications without VIBRATE permission should degrade to no-op.
}
}

private static Vibrator getVibrator() {
View currentView = view;
if (currentView == null || currentView.getContext() == null) {
return null;
}
return (Vibrator) currentView.getContext().getSystemService(Context.VIBRATOR_SERVICE);
}

@Override
public void showSoftKeyboard(final boolean show) {
view.getHandler().post(new Runnable() {
Expand Down
Loading
Loading