diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index b0414a0d29..7e860baf3f 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -8,7 +8,7 @@ jobs: if: ${{ false }} steps: - name: Checkout - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Prettify code diff --git a/.github/workflows/j3o-scan.yml b/.github/workflows/j3o-scan.yml index f724e217d7..33ccbc1314 100644 --- a/.github/workflows/j3o-scan.yml +++ b/.github/workflows/j3o-scan.yml @@ -28,10 +28,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: temurin java-version: '17' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5a11a6f244..aa21387b7b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,14 +64,14 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@v6 - name: Setup the java environment - uses: actions/setup-java@v5.2.0 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '21' - name: Validate the Gradle wrapper - uses: gradle/actions/wrapper-validation@v6.0.1 + uses: gradle/actions/wrapper-validation@v6.1.0 - name: Run Checkstyle run: | ./gradlew checkstyleMain checkstyleTest --console=plain --stacktrace @@ -91,14 +91,14 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@v6 - name: Setup the java environment - uses: actions/setup-java@v5.2.0 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '21' - name: Validate the Gradle wrapper - uses: gradle/actions/wrapper-validation@v6.0.1 + uses: gradle/actions/wrapper-validation@v6.1.0 - name: Run SpotBugs run: | ./gradlew -PenableSpotBugs=true spotbugsMain spotbugsTest --console=plain --stacktrace @@ -119,25 +119,27 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v6.0.2 - - name: Start xvfb + - uses: actions/checkout@v6 + - name: Validate the Gradle wrapper + uses: gradle/actions/wrapper-validation@v6.1.0 + + - name: Start xvfb, debug, and run tests run: | - Xvfb :99 -ac -screen 0 1024x768x16 & + Xvfb :99 -ac -screen 0 1024x768x24/4 & export DISPLAY=:99 echo "DISPLAY=:99" >> $GITHUB_ENV - - name: Report GL/Vulkan - run: | + + # Debug GL/Vulkan set -x echo "DISPLAY=$DISPLAY" - glxinfo | grep -E "OpenGL version|OpenGL renderer|OpenGL vendor" || true + glxinfo | grep -E "OpenGL version|OpenGL renderer|OpenGL vendor|visual|GLX version" || true vulkaninfo --summary || true echo "VK_ICD_FILENAMES=$VK_ICD_FILENAMES" echo "MESA_LOADER_DRIVER_OVERRIDE=$MESA_LOADER_DRIVER_OVERRIDE" echo "GALLIUM_DRIVER=$GALLIUM_DRIVER" - - name: Validate the Gradle wrapper - uses: gradle/actions/wrapper-validation@v6.0.1 - - name: Test with Gradle Wrapper - run: | + echo "LIBGL_ALWAYS_SOFTWARE=$LIBGL_ALWAYS_SOFTWARE" + + # Run tests ./gradlew :jme3-screenshot-test:screenshotTest - name: Upload Test Reports uses: actions/upload-artifact@v7.0.0 @@ -160,7 +162,7 @@ jobs: 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.2.0 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '17' @@ -171,12 +173,12 @@ jobs: xcode-version: '15.1.0' - name: Clone the repo - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6 with: fetch-depth: 1 - name: Validate the Gradle wrapper - uses: gradle/actions/wrapper-validation@v6.0.1 + uses: gradle/actions/wrapper-validation@v6.1.0 - name: Build run: | @@ -198,12 +200,12 @@ jobs: steps: - name: Clone the repo - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6 with: fetch-depth: 1 - name: Setup Java 17 - uses: actions/setup-java@v5.2.0 + uses: actions/setup-java@v5 with: distribution: temurin java-version: '17' @@ -218,7 +220,7 @@ jobs: cmake --version - name: Validate the Gradle wrapper - uses: gradle/actions/wrapper-validation@v6.0.1 + uses: gradle/actions/wrapper-validation@v6.1.0 - name: Build run: | @@ -255,7 +257,7 @@ jobs: steps: - name: Clone the repo - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6 with: fetch-depth: 1 @@ -272,7 +274,7 @@ jobs: 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.0.1 + uses: gradle/actions/wrapper-validation@v6.1.0 - name: Build Engine shell: bash run: | @@ -447,13 +449,13 @@ jobs: # We need to clone everything again for uploadToMaven.sh ... - name: Clone the repo - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6 with: fetch-depth: 1 # Setup jdk 21 used for building Maven-style artifacts - name: Setup the java environment - uses: actions/setup-java@v5.2.0 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '21' @@ -497,13 +499,13 @@ jobs: # We need to clone everything again for uploadToCentral.sh ... - name: Clone the repo - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6 with: fetch-depth: 1 # Setup jdk 21 used for building Sonatype artifacts - name: Setup the java environment - uses: actions/setup-java@v5.2.0 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '21' diff --git a/.gitignore b/.gitignore index a15615de45..a202c577b9 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,4 @@ javadoc_deploy.pub !.vscode/settings.json !.vscode/JME_style.xml !.vscode/extensions.json -joysticks-*.txt \ No newline at end of file +joysticks-*.txt diff --git a/.vscode/settings.json b/.vscode/settings.json index 6e9df573c0..e559d94247 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,5 @@ "editor.formatOnPaste": false, "editor.formatOnType": false, "editor.formatOnSave": false, - "java.jdt.ls.vmargs": "-Xms512M -Xmx2G -XX:+UseG1GC -XX:+UseStringDeduplication -XX:AdaptiveSizePolicyWeight=90" + "java.jdt.ls.vmargs": "-Xms512M -Xmx4G -XX:+UseG1GC -XX:+UseStringDeduplication -XX:AdaptiveSizePolicyWeight=90" } diff --git a/README.md b/README.md index 5718e31e09..aedfb69fe6 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,18 @@ You can optionally use the `-Pexample` property to specify an example to start w ./gradlew runExamples -Pexample=jme3test.light.pbr.TestPBRSimple ``` +### Android examples + +You can run the Android examples on a local android emulator with: + +```bash +./gradlew -PbuildAndroidExamples=true -PbuildNativeProjects=true runAndroidExamples +# or for a specific example: +# ./gradlew -PbuildAndroidExamples=true -PbuildNativeProjects=true runAndroidExamples -Pexample=jme3test.post.TestBloom +``` + +*Make sure to have the SDK and NDK installed and configured properly, and the emulator running before executing the command.* + ## Running Tests diff --git a/build.gradle b/build.gradle index 113b992314..8dade8c48e 100644 --- a/build.gradle +++ b/build.gradle @@ -182,17 +182,56 @@ task configureAndroidNDK { ndkBuildFile = "ndk-build.cmd" } - // ndkPath is defined in the root project gradle.properties file - String ndkBuildPath = ndkPath + File.separator + ndkBuildFile - //Use the environment variable for the NDK location if defined - if (System.env.ANDROID_NDK != null) { - ndkBuildPath = System.env.ANDROID_NDK + File.separator + ndkBuildFile + def ndkCandidates = [] + if (System.env.ANDROID_NDK) { + ndkCandidates << file(System.env.ANDROID_NDK) + } + if (System.env.ANDROID_NDK_HOME) { + ndkCandidates << file(System.env.ANDROID_NDK_HOME) + } + if (project.hasProperty('ndkPath') && ndkPath) { + ndkCandidates << file(ndkPath) } - if (new File(ndkBuildPath).exists()) { + def localProperties = file('local.properties') + if (localProperties.isFile()) { + Properties properties = new Properties() + localProperties.withInputStream { properties.load(it) } + if (properties.getProperty('ndk.dir')) { + ndkCandidates << file(properties.getProperty('ndk.dir')) + } + if (properties.getProperty('sdk.dir')) { + ndkCandidates.addAll(findAndroidNdkDirs(file(properties.getProperty('sdk.dir')))) + } + } + + if (System.env.ANDROID_HOME) { + ndkCandidates.addAll(findAndroidNdkDirs(file(System.env.ANDROID_HOME))) + } + if (System.env.ANDROID_SDK_ROOT) { + ndkCandidates.addAll(findAndroidNdkDirs(file(System.env.ANDROID_SDK_ROOT))) + } + ndkCandidates.addAll(findAndroidNdkDirs(file("${System.properties['user.home']}/Android/Sdk"))) + + def ndkBuildPath = ndkCandidates.collect { new File(it, ndkBuildFile) }.find { it.isFile() } + if (ndkBuildPath != null) { ndkExists = true - ndkCommandPath = ndkBuildPath + ndkCommandPath = ndkBuildPath.absolutePath + } +} + +def findAndroidNdkDirs(File sdkDir) { + def ndkDirs = [] + def nestedSdkDir = new File(sdkDir, 'Sdk') + if (nestedSdkDir.isDirectory()) { + ndkDirs.addAll(findAndroidNdkDirs(nestedSdkDir)) + } + def sideBySideDir = new File(sdkDir, 'ndk') + if (sideBySideDir.isDirectory()) { + ndkDirs.addAll(sideBySideDir.listFiles()?.findAll { it.isDirectory() }?.sort { a, b -> b.name <=> a.name } ?: []) } + ndkDirs << new File(sdkDir, 'ndk-bundle') + return ndkDirs } gradle.rootProject.ext.set("usePrebuildNatives", buildNativeProjects!="true"); diff --git a/common-android-app.gradle b/common-android-app.gradle index 703536bd27..8670bf7e78 100644 --- a/common-android-app.gradle +++ b/common-android-app.gradle @@ -6,6 +6,6 @@ version = jmeFullVersion repositories { mavenCentral() maven { - url "http://nifty-gui.sourceforge.net/nifty-maven-repo" + url "https://nifty-gui.sourceforge.net/nifty-maven-repo" } } diff --git a/common.gradle b/common.gradle index 05400cdaac..4477404421 100644 --- a/common.gradle +++ b/common.gradle @@ -36,6 +36,12 @@ repositories { flatDir { dirs rootProject.file('lib') } + maven { + url "https://maven.rblb.it/NostrGameEngine/angle-natives" + } + maven { + url = uri("https://central.sonatype.com/repository/maven-snapshots") + } } dependencies { diff --git a/gradle/jacoco.gradle b/gradle/jacoco.gradle index f7f2e48481..5491d4eb55 100644 --- a/gradle/jacoco.gradle +++ b/gradle/jacoco.gradle @@ -61,7 +61,9 @@ reporting { def jacocoSubprojects = subprojects.findAll { p -> (p.file("src/main/java").exists() || p.file("src/main/groovy").exists()) && - (p.file("src/test/java").exists() || p.file("src/test/groovy").exists()) + (p.file("src/test/java").exists() || p.file("src/test/groovy").exists()) && + p.tasks.findByName('test') && + p.tasks.findByName('jacocoTestReport') } dependencies { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5151b44419..0a38189461 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,15 +5,19 @@ checkstyle = "13.3.0" jacoco = "0.8.12" lwjgl3 = "3.4.1" +angle = "20260507.46e42d40740c-SNAPSHOT" +saferalloc = "0.0.8" nifty = "1.4.3" spotbugs = "4.9.8" [libraries] androidx-annotation = "androidx.annotation:annotation:1.7.1" +androidx-fragment = "androidx.fragment:fragment:1.8.9" androidx-lifecycle-common = "androidx.lifecycle:lifecycle-common:2.7.0" android-build-gradle = "com.android.tools.build:gradle:9.1.0" android-support-appcompat = "com.android.support:appcompat-v7:28.0.0" +androidx-test-runner = "androidx.test:runner:1.7.0" gradle-git = "org.ajoberstar:gradle-git:1.2.0" groovy-test = "org.apache.groovy:groovy-test:4.0.31" gson = "com.google.code.gson:gson:2.13.2" @@ -23,6 +27,7 @@ jinput = "net.java.jinput:jinput:2.0.9" jna = "net.java.dev.jna:jna:5.18.1" jnaerator-runtime = "com.nativelibs4java:jnaerator-runtime:0.12" junit-bom = "org.junit:junit-bom:5.13.4" +junit4 = "junit:junit:4.13.2" junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" } junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher" } lwjgl2 = "org.jmonkeyengine:lwjgl:2.9.5" @@ -33,9 +38,10 @@ lwjgl3-glfw = { module = "org.lwjgl:lwjgl-glfw", version.ref = "lwjgl3" lwjgl3-jawt = { module = "org.lwjgl:lwjgl-jawt", version.ref = "lwjgl3" } lwjgl3-jemalloc = { module = "org.lwjgl:lwjgl-jemalloc", version.ref = "lwjgl3" } lwjgl3-openal = { module = "org.lwjgl:lwjgl-openal", version.ref = "lwjgl3" } -lwjgl3-opencl = { module = "org.lwjgl:lwjgl-opencl", version.ref = "lwjgl3" } lwjgl3-opengl = { module = "org.lwjgl:lwjgl-opengl", version.ref = "lwjgl3" } lwjgl3-sdl = { module = "org.lwjgl:lwjgl-sdl", version.ref = "lwjgl3" } +lwjgl3-opengles = { module = "org.lwjgl:lwjgl-opengles", version.ref = "lwjgl3" } +lwjgl3-egl = { module = "org.lwjgl:lwjgl-egl", version.ref = "lwjgl3" } mokito-core = "org.mockito:mockito-core:5.23.0" mokito-junit-jupiter = "org.mockito:mockito-junit-jupiter:5.23.0" @@ -50,8 +56,18 @@ vecmath = "javax.vecmath:vecmath:1.5.2" stb-image = "org.ngengine:stb-image:2.30.4" imagewebp = "org.ngengine:image-webp-decoder:1.3.0" +angle = { module = "org.ngengine:angle-natives", version.ref = "angle" } +saferalloc = { module = "org.ngengine:saferalloc", version.ref = "saferalloc" } +saferalloc-natives-linux-x8664 = { module = "org.ngengine:saferalloc-natives-linux-x86_64", version.ref = "saferalloc" } +saferalloc-natives-linux-aarch64 = { module = "org.ngengine:saferalloc-natives-linux-aarch64", version.ref = "saferalloc" } +saferalloc-natives-windows-x8664 = { module = "org.ngengine:saferalloc-natives-windows-x86_64", version.ref = "saferalloc" } +saferalloc-natives-windows-aarch64 = { module = "org.ngengine:saferalloc-natives-windows-aarch64", version.ref = "saferalloc" } +saferalloc-natives-macos-x8664 = { module = "org.ngengine:saferalloc-natives-macos-x86_64", version.ref = "saferalloc" } +saferalloc-natives-macos-aarch64 = { module = "org.ngengine:saferalloc-natives-macos-aarch64", version.ref = "saferalloc" } +saferalloc-natives-android = { module = "org.ngengine:saferalloc-natives-android", version.ref = "saferalloc" } [bundles] +saferalloc = ["saferalloc", "saferalloc-natives-linux-x8664", "saferalloc-natives-linux-aarch64", "saferalloc-natives-windows-x8664", "saferalloc-natives-windows-aarch64", "saferalloc-natives-macos-x8664", "saferalloc-natives-macos-aarch64", "saferalloc-natives-android"] [plugins] jacoco = { id = "jacoco", version.ref = "jacoco" } diff --git a/jme3-android-examples/build.gradle b/jme3-android-examples/build.gradle index 66c591a931..a795b54e2d 100644 --- a/jme3-android-examples/build.gradle +++ b/jme3-android-examples/build.gradle @@ -1,5 +1,7 @@ apply plugin: 'com.android.application' +def examplesJar = project(':jme3-examples').tasks.named('jar') + android { namespace "org.jmonkeyengine.jme3androidexamples" compileSdk 34 @@ -16,12 +18,13 @@ android { targetSdk 34 // Android 14 versionCode 1 versionName "1.0" // TODO: from settings.gradle + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } @@ -40,23 +43,117 @@ android { srcDir '../jme3-testdata/src/main/resources' srcDir '../jme3-examples/src/main/resources' } + jniLibs { + srcDir '../build/native/openalsoft' + srcDir '../build/native/decode' + srcDir '../build/native/allocator' + } } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - testImplementation libs.junit4 - implementation libs.androidx.appcompat - + testImplementation platform(libs.junit.bom) + testImplementation libs.junit.jupiter + testRuntimeOnly libs.junit.platform.launcher + androidTestImplementation libs.junit4 + androidTestImplementation libs.androidx.test.runner + implementation libs.androidx.fragment implementation project(':jme3-core') implementation project(':jme3-android') implementation project(':jme3-android-native') implementation project(':jme3-effects') implementation project(':jme3-jbullet') + implementation project(':jme3-jogg') implementation project(':jme3-networking') implementation project(':jme3-niftygui') implementation project(':jme3-plugins') + implementation project(':jme3-plugins-json') + implementation project(':jme3-plugins-json-gson') implementation project(':jme3-terrain') - implementation fileTree(dir: '../jme3-examples/build/libs', include: ['*.jar'], exclude: ['*sources*.*']) + implementation files(examplesJar.flatMap { it.archiveFile }) +} + +tasks.named('preBuild') { + dependsOn examplesJar + if (buildNativeProjects == "true") { + dependsOn ':jme3-android-native:updatePreCompiledOpenAlSoftLibs' + dependsOn ':jme3-android-native:updatePreCompiledLibs' + dependsOn ':jme3-android-native:updatePreCompiledLibsBufferAllocator' + } else if (skipPrebuildLibraries != "true") { + dependsOn ':jme3-android-native:copyPreCompiledOpenAlSoftLibs' + dependsOn ':jme3-android-native:copyPreCompiledLibs' + dependsOn ':jme3-android-native:copyPreCompiledLibsBufferAllocator' + } +} + +tasks.register('installAndroidExamples', Exec) { + group = 'application' + description = 'Install the Android examples selector on a connected emulator or device.' + + dependsOn tasks.named('assembleDebug') + + doFirst { + def adbExecutable = findAdbExecutable() + if (adbExecutable == null) { + throw new GradleException("ADB not found. Set -Pandroid.sdk.path, ANDROID_HOME, or ANDROID_SDK_ROOT.") + } + def apkFile = layout.buildDirectory.file('outputs/apk/debug/jme3-android-examples-debug.apk').get().asFile + if (!apkFile.isFile()) { + throw new GradleException("Debug APK not found at ${apkFile}.") + } + executable adbExecutable.absolutePath + args 'install', '-r', apkFile.absolutePath + } +} + +tasks.register('runAndroidExamples', Exec) { + group = 'application' + description = 'Install and launch Android examples on a connected emulator or device. Use -Pexample= to run a specific test directly.' + + dependsOn tasks.named('installAndroidExamples') + + doFirst { + def adbExecutable = findAdbExecutable() + if (adbExecutable == null) { + throw new GradleException("ADB not found. Set -Pandroid.sdk.path, ANDROID_HOME, or ANDROID_SDK_ROOT.") + } + executable adbExecutable.absolutePath + + def exampleClass = project.findProperty('example')?.toString()?.trim() + if (exampleClass) { + args 'shell', 'am', 'start', + '-n', 'org.jmonkeyengine.jme3androidexamples/.TestActivity', + '--es', 'Selected_App_Class', exampleClass, + '--ez', 'Enable_Mouse_Events', 'true', + '--ez', 'Enable_Joystick_Events', 'false', + '--ez', 'Enable_Key_Events', 'true', + '--ez', 'Verbose_Logging', 'false' + } else { + args 'shell', 'am', 'start', + '-n', 'org.jmonkeyengine.jme3androidexamples/.MainActivity' + } + } +} + +def findAdbExecutable() { + def adbName = System.properties['os.name'].toLowerCase().contains('windows') ? 'adb.exe' : 'adb' + def sdkDirs = [] + if (project.findProperty('android.sdk.path')) { + sdkDirs << file(project.findProperty('android.sdk.path')) + } + if (System.env.ANDROID_HOME) { + sdkDirs << file(System.env.ANDROID_HOME) + } + if (System.env.ANDROID_SDK_ROOT) { + sdkDirs << file(System.env.ANDROID_SDK_ROOT) + } + sdkDirs << file("${System.properties['user.home']}/Android/Sdk") + + def expandedSdkDirs = sdkDirs.collectMany { sdkDir -> + [sdkDir, new File(sdkDir, 'Sdk')] + }.unique { it.absolutePath } + + return expandedSdkDirs.collect { new File(new File(it, 'platform-tools'), adbName) }.find { it.isFile() } } diff --git a/jme3-android-examples/src/androidTest/java/org/jmonkeyengine/jme3androidexamples/ApplicationTest.java b/jme3-android-examples/src/androidTest/java/org/jmonkeyengine/jme3androidexamples/ApplicationTest.java index dff82ddb86..b10ebf4789 100644 --- a/jme3-android-examples/src/androidTest/java/org/jmonkeyengine/jme3androidexamples/ApplicationTest.java +++ b/jme3-android-examples/src/androidTest/java/org/jmonkeyengine/jme3androidexamples/ApplicationTest.java @@ -1,13 +1,19 @@ package org.jmonkeyengine.jme3androidexamples; -import android.app.Application; -import android.test.ApplicationTestCase; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; /** * Testing Fundamentals */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); +public class ApplicationTest { + @Test + public void testApplicationPackage() { + String packageName = InstrumentationRegistry.getInstrumentation() + .getTargetContext() + .getPackageName(); + assertEquals("org.jmonkeyengine.jme3androidexamples", packageName); } -} \ No newline at end of file +} diff --git a/jme3-android-examples/src/main/AndroidManifest.xml b/jme3-android-examples/src/main/AndroidManifest.xml index 95789aa79c..075d2a515c 100644 --- a/jme3-android-examples/src/main/AndroidManifest.xml +++ b/jme3-android-examples/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ android:theme="@style/AppTheme"> @@ -19,15 +20,16 @@ - + @@ -41,6 +43,7 @@ android:normalScreens="true" android:smallScreens="true"/> + diff --git a/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/JmeFragment.java b/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/JmeFragment.java index 0e2faeea3a..3a3e399930 100644 --- a/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/JmeFragment.java +++ b/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/JmeFragment.java @@ -2,6 +2,8 @@ import android.os.Bundle; import com.jme3.app.AndroidHarnessFragment; +import com.jme3.app.LegacyApplication; +import com.jme3.system.AppSettings; import java.util.logging.Level; import java.util.logging.LogManager; @@ -9,53 +11,14 @@ * A placeholder fragment containing a jME GLSurfaceView. */ public class JmeFragment extends AndroidHarnessFragment { + private String appClass; + private boolean joystickEventsEnabled; + private boolean keyEventsEnabled = true; + private boolean mouseEventsEnabled = true; public JmeFragment() { - // Set the desired EGL configuration - eglBitsPerPixel = 24; - eglAlphaBits = 0; - eglDepthBits = 16; - eglSamples = 0; - eglStencilBits = 0; - - // Set the maximum framerate - // (default = -1 for unlimited) - frameRate = -1; - - // Set the maximum resolution dimension - // (the smaller side, height or width, is set automatically - // to maintain the original device screen aspect ratio) - // (default = -1 to match device screen resolution) - maxResolutionDimension = -1; - - /* - Skip these settings and use the settings stored in the Bundle retrieved during onCreate. - - // Set main project class (fully qualified path) - appClass = ""; - - // Set input configuration settings - joystickEventsEnabled = false; - keyEventsEnabled = true; - mouseEventsEnabled = true; - */ - - // Set application exit settings finishOnAppStop = true; - handleExitHook = true; - exitDialogTitle = "Do you want to exit?"; - exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it."; - - // Set splash screen resource id, if used - // (default = 0, no splash screen) - // For example, if the image file name is "splash"... - // splashPicID = R.drawable.splash; - splashPicID = 0; -// splashPicID = R.drawable.android_splash; - - // Set the default logging level (default=Level.INFO, Level.ALL=All Debug Info) LogManager.getLogManager().getLogger("").setLevel(Level.INFO); - } @Override @@ -82,4 +45,25 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } + + @Override + protected LegacyApplication createApplication() throws Exception { + Class clazz = Class.forName(appClass); + return (LegacyApplication) clazz.getDeclaredConstructor().newInstance(); + } + + @Override + protected void configureSettings(AppSettings settings) { + settings.setEmulateMouse(mouseEventsEnabled); + settings.setUseJoysticks(joystickEventsEnabled); + settings.setEmulateKeyboard(keyEventsEnabled); + + settings.setBitsPerPixel(24); + settings.setAlphaBits(0); + settings.setGammaCorrection(true); + settings.setDepthBits(16); + settings.setSamples(4); + settings.setStencilBits(0); + settings.setFrameRate(-1); + } } diff --git a/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/MainActivity.java b/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/MainActivity.java index d02f7527ca..960e53bc43 100644 --- a/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/MainActivity.java +++ b/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/MainActivity.java @@ -2,8 +2,8 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.app.Activity; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -20,6 +20,7 @@ import dalvik.system.DexFile; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; import java.util.List; @@ -30,7 +31,7 @@ * applications that are started via TestsHarness Activity. * @author iwgeric */ -public class MainActivity extends AppCompatActivity implements OnItemClickListener, View.OnClickListener, TextWatcher { +public class MainActivity extends Activity implements OnItemClickListener, View.OnClickListener, TextWatcher { private static final String TAG = "MainActivity"; /** @@ -127,13 +128,14 @@ public void onCreate(Bundle savedInstanceState) { editFilterText = (EditText) findViewById(R.id.txtFilter); - /* Define the root package to start with */ + /* Define the root package shared by the desktop and Android examples. */ rootPackage = "jme3test"; /* Create an array of Strings to define which classes to exclude */ exclusions.add("$"); // inner classes exclusions.add("TestChooser"); // Desktop test chooser class exclusions.add("awt"); // Desktop test chooser class + exclusions.add("package-info"); // mExclusions.add(""); @@ -147,24 +149,25 @@ public void onCreate(Bundle savedInstanceState) { ApplicationInfo ai = this.getApplicationInfo(); String classPath = ai.sourceDir; DexFile dex = null; - Enumeration apkClassNames = null; try { dex = new DexFile(classPath); - apkClassNames = dex.entries(); + Enumeration apkClassNames = dex.entries(); while (apkClassNames.hasMoreElements()) { String className = apkClassNames.nextElement(); - if (checkClassName(className) && checkClassType(className)) { + if (checkClassName(className) && checkClassType(className) && !classNames.contains(className)) { classNames.add(className); } -// classNames.add(className); } + Collections.sort(classNames); } catch (IOException e) { e.printStackTrace(); } finally { - try { - dex.close(); - } catch (IOException e) { - e.printStackTrace(); + if (dex != null) { + try { + dex.close(); + } catch (IOException e) { + e.printStackTrace(); + } } } @@ -286,7 +289,7 @@ private boolean checkClassName(String className) { private boolean checkClassType(String className) { boolean include = true; try { - Class clazz = Class.forName(className); + Class clazz = Class.forName(className, false, getClassLoader()); if (Application.class.isAssignableFrom(clazz)) { Log.d(TAG, "Class " + className + " is a jME Application"); } else { @@ -294,9 +297,9 @@ private boolean checkClassType(String className) { Log.d(TAG, "Skipping Class " + className + ". Not a jME Application"); } - } catch (NoClassDefFoundError ncdf) { + } catch (LinkageError ncdf) { include = false; - Log.d(TAG, "Skipping Class " + className + ". No Class Def found."); + Log.d(TAG, "Skipping Class " + className + ". Could not link class."); } catch (ClassNotFoundException cnfe) { include = false; Log.d(TAG, "Skipping Class " + className + ". Class not found."); @@ -430,29 +433,25 @@ public boolean onPrepareOptionsMenu (Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.optionMouseEvents: - enableMouseEvents = !enableMouseEvents; - Log.d(TAG, "enableMouseEvents set to: " + enableMouseEvents); - break; - case R.id.optionJoystickEvents: - enableJoystickEvents = !enableJoystickEvents; - Log.d(TAG, "enableJoystickEvents set to: " + enableJoystickEvents); - break; - case R.id.optionKeyEvents: - enableKeyEvents = !enableKeyEvents; - Log.d(TAG, "enableKeyEvents set to: " + enableKeyEvents); - break; - case R.id.optionVerboseLogging: - verboseLogging = !verboseLogging; - Log.d(TAG, "verboseLogging set to: " + verboseLogging); - break; - default: - return super.onOptionsItemSelected(item); + int itemId = item.getItemId(); + if (itemId == R.id.optionMouseEvents) { + enableMouseEvents = !enableMouseEvents; + Log.d(TAG, "enableMouseEvents set to: " + enableMouseEvents); + } else if (itemId == R.id.optionJoystickEvents) { + enableJoystickEvents = !enableJoystickEvents; + Log.d(TAG, "enableJoystickEvents set to: " + enableJoystickEvents); + } else if (itemId == R.id.optionKeyEvents) { + enableKeyEvents = !enableKeyEvents; + Log.d(TAG, "enableKeyEvents set to: " + enableKeyEvents); + } else if (itemId == R.id.optionVerboseLogging) { + verboseLogging = !verboseLogging; + Log.d(TAG, "verboseLogging set to: " + verboseLogging); + } else { + return super.onOptionsItemSelected(item); } return true; } -} \ No newline at end of file +} diff --git a/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/TestActivity.java b/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/TestActivity.java index 70e7075705..1251c94118 100644 --- a/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/TestActivity.java +++ b/jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/TestActivity.java @@ -1,14 +1,14 @@ package org.jmonkeyengine.jme3androidexamples; -import android.app.FragmentTransaction; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentTransaction; import com.jme3.system.JmeSystem; -public class TestActivity extends AppCompatActivity { +public class TestActivity extends FragmentActivity { JmeFragment fragment; @Override @@ -49,7 +49,7 @@ protected void onCreate(Bundle savedInstanceState) { fragment.setArguments(args); - FragmentTransaction transaction = getFragmentManager().beginTransaction(); + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); // Replace whatever is in the fragment_container view with this fragment, // and add the transaction to the back stack so the user can navigate back @@ -83,13 +83,11 @@ public boolean onPrepareOptionsMenu (Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.optionToggleKeyboard: - toggleKeyboard(true); -// Log.d(this.getClass().getSimpleName(), "showing soft keyboard"); - break; - default: - return super.onOptionsItemSelected(item); + if (item.getItemId() == R.id.optionToggleKeyboard) { + toggleKeyboard(true); +// Log.d(this.getClass().getSimpleName(), "showing soft keyboard"); + } else { + return super.onOptionsItemSelected(item); } return true; diff --git a/jme3-android-examples/src/main/res/layout-land/test_chooser_layout.xml b/jme3-android-examples/src/main/res/layout-land/test_chooser_layout.xml new file mode 100644 index 0000000000..4285fa3c5f --- /dev/null +++ b/jme3-android-examples/src/main/res/layout-land/test_chooser_layout.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + +