From b65c6177da9d122427f8932b856d0bc8ae3179a2 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Thu, 4 Sep 2025 11:06:35 +0200 Subject: [PATCH] add android target and update repo structure --- .gitignore | 4 +- .vscode/launch.json | 22 ++ app/build.gradle | 270 +-------------- .../{demo/adc/AdcDemo.java => app/Main.java} | 26 +- .../org/ngengine/demo/adc/MapComponent.java | 1 + gradle.properties | 18 +- gradle/libs.versions.toml | 17 + local.properties | 8 + platform-android/build.gradle | 58 ++++ .../src/androidTest/java/.gitkeep | 0 platform-android/src/main/AndroidManifest.xml | 27 ++ .../platform/AndroidLauncherActivity.java | 36 ++ .../platform/AndroidLauncherFragment.java | 308 ++++++++++++++++++ .../drawable-v24/ic_launcher_foreground.xml | 30 ++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++ .../res/layout/activity_android_launcher.xml | 15 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 2513 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 5175 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 1778 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 3184 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 3477 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 7138 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 5311 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 10718 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 6990 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 14156 bytes platform-android/src/test/java/.gitkeep | 0 platform-desktop/build.gradle | 268 +++++++++++++++ .../graal.initialize-at-runtime.conf | 0 .../main/java/org/ngengine/platform/.gitkeep | 0 .../ngengine/platform/DesktopLauncher.java | 14 + platform-desktop/src/main/resources/.gitkeep | 0 platform-desktop/src/test/java/.gitkeep | 0 .../trace/jni-config.json | 0 .../trace/predefined-classes-config.json | 0 .../trace/proxy-config.json | 0 .../trace/reachability-metadata.json | 0 .../trace/reflect-config.json | 0 .../trace/resource-config.json | 0 .../trace/serialization-config.json | 0 settings.gradle | 41 ++- 43 files changed, 1064 insertions(+), 279 deletions(-) create mode 100644 .vscode/launch.json rename app/src/main/java/org/ngengine/{demo/adc/AdcDemo.java => app/Main.java} (68%) create mode 100644 local.properties create mode 100644 platform-android/build.gradle create mode 100644 platform-android/src/androidTest/java/.gitkeep create mode 100644 platform-android/src/main/AndroidManifest.xml create mode 100644 platform-android/src/main/java/org/ngengine/platform/AndroidLauncherActivity.java create mode 100644 platform-android/src/main/java/org/ngengine/platform/AndroidLauncherFragment.java create mode 100644 platform-android/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 platform-android/src/main/res/drawable/ic_launcher_background.xml create mode 100644 platform-android/src/main/res/layout/activity_android_launcher.xml create mode 100644 platform-android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 platform-android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 platform-android/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 platform-android/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 platform-android/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 platform-android/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 platform-android/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 platform-android/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 platform-android/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 platform-android/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 platform-android/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 platform-android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 platform-android/src/test/java/.gitkeep create mode 100644 platform-desktop/build.gradle rename {app => platform-desktop}/graal.initialize-at-runtime.conf (100%) create mode 100644 platform-desktop/src/main/java/org/ngengine/platform/.gitkeep create mode 100644 platform-desktop/src/main/java/org/ngengine/platform/DesktopLauncher.java create mode 100644 platform-desktop/src/main/resources/.gitkeep create mode 100644 platform-desktop/src/test/java/.gitkeep rename {app => platform-desktop}/trace/jni-config.json (100%) rename {app => platform-desktop}/trace/predefined-classes-config.json (100%) rename {app => platform-desktop}/trace/proxy-config.json (100%) rename {app => platform-desktop}/trace/reachability-metadata.json (100%) rename {app => platform-desktop}/trace/reflect-config.json (100%) rename {app => platform-desktop}/trace/resource-config.json (100%) rename {app => platform-desktop}/trace/serialization-config.json (100%) diff --git a/.gitignore b/.gitignore index 805116e..46d75ad 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ # Ignore Gradle build output directory build bin -dist \ No newline at end of file +dist +*.hprof +.idea \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..acc35fb --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "java", + "name": "Current File", + "request": "launch", + "mainClass": "${file}" + }, + { + "type": "java", + "name": "DesktopLauncher", + "request": "launch", + "mainClass": "org.ngengine.platform.DesktopLauncher", + "projectName": "platform-desktop" + } + ] +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 7f97408..e63f4f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,288 +1,28 @@ plugins { - id 'application' - id 'org.graalvm.buildtools.native' version '0.10.6' - id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'java' } -repositories { - mavenLocal() - mavenCentral() - maven { - url = uri("https://central.sonatype.com/repository/maven-snapshots") - } - maven { - url = "https://maven.rblb.it/NostrGameEngine/libdatachannel-java" - } -} dependencies { testImplementation libs.junit implementation "org.ngengine:nge-app:${ngeVersion}" - implementation "org.ngengine:nge-app-jvm:${ngeVersion}" implementation "org.ngengine:nge-auth:${ngeVersion}" implementation "org.ngengine:nge-core:${ngeVersion}" implementation "org.ngengine:nge-gui:${ngeVersion}" implementation "org.ngengine:nge-networking:${ngeVersion}" implementation "org.ngengine:nge-ads:${ngeVersion}" - implementation "org.ngengine:jme3-desktop:${ngeVersion}" implementation "org.ngengine:jme3-jbullet:${ngeVersion}" implementation "org.ngengine:jme3-effects:${ngeVersion}" implementation "org.ngengine:jme3-jogg:${ngeVersion}" - implementation "org.ngengine:jme3-lwjgl3:${ngeVersion}" implementation "org.ngengine:jme3-plugins:${ngeVersion}" } + java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - vendor = JvmVendorSpec.ADOPTIUM - implementation = JvmImplementation.VENDOR_SPECIFIC - } -} - -application { - mainClass = project.findProperty('mainClass') -} - -run { - jvmArgs = [ - "-Djava.util.logging.config.file=${rootProject.projectDir}/dev-logging.properties", - '-ea' - ] - if (getOsName() == 'macos') { - jvmArgs('-XstartOnFirstThread') - } -} - -shadowJar { - archiveBaseName = "${rootProject.name}" - archiveClassifier = 'portable' - archiveVersion = "${project.version}" - destinationDirectory = file("${rootProject.projectDir}/dist/portable") - manifest { - attributes 'Main-Class': application.mainClass.get() - } - mergeServiceFiles() -} - - -build.dependsOn shadowJar - -graalvmNative { - toolchainDetection = true - metadataRepository { - enabled = true - } - binaries { - main { - def buildName = "${rootProject.name}"; - imageName = buildName - def initAtRuntime = file("${project.projectDir}/graal.initialize-at-runtime.conf") - .text - .split("\\r?\\n") - .findAll { it && !it.startsWith('#') && !it.trim().isEmpty() } - .join(',') - - mainClass = application.mainClass.get() - def arguments = [ - '--report-unsupported-elements-at-runtime', - '--install-exit-handlers', - '--add-exports=java.desktop/sun.awt=ALL-UNNAMED', - '--add-exports=java.desktop/sun.java2d=ALL-UNNAMED', - '--add-exports=java.base/sun.nio.ch=ALL-UNNAMED', - '-H:+ReportExceptionStackTraces', - '-H:+PrintClassInitialization', - '--initialize-at-run-time=' + initAtRuntime, - '--no-server', - '--enable-url-protocols=http,https,file', - '--link-at-build-time', - '-H:+UnlockExperimentalVMOptions', - '--enable-native-access=ALL-UNNAMED', - '-H:+StaticExecutableWithDynamicLibC', - '-march=compatibility', - '-Djava.awt.headless=true', - '-H:IncludeResources=.*', - '-H:Name=' + buildName, - "-H:ConfigurationFileDirectories=${project.projectDir}/trace" - ] - - // Add G1 GC only if linux - if (org.gradle.internal.os.OperatingSystem.current().isLinux()) { - arguments.add('--gc=G1') - } - - buildArgs.addAll(arguments) - sharedLibrary = false - } - } -} - -configurations { - nativeImageCompileOnly { - canBeResolved = true - } -} - -def getOsName(){ - def osName = org.gradle.internal.os.OperatingSystem.current().getName().toLowerCase() - - if (org.gradle.internal.os.OperatingSystem.current().isWindows()){ - osName = "windows" - } else if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) { - osName = "macos" - } else if (org.gradle.internal.os.OperatingSystem.current().isLinux()) { - osName = "linux" - } - - return osName -} - - -task traceNative(type: JavaExec) { - group = 'graalvm' - description = 'Trace reflection and resource usage' - mainClass = application.mainClass.get() - classpath = sourceSets.main.runtimeClasspath - jvmArgs = [ - "-agentlib:native-image-agent=config-merge-dir=${project.projectDir}/trace", - '-Djava.awt.headless=true', - ] - - if (getOsName() == 'macos') { - jvmArgs('-XstartOnFirstThread') - } - - systemProperty 'java.awt.headless', 'false' - systemProperty 'testMode', 'true' - doFirst { - println "Tracing to: ${project.projectDir}/trace" - println "Run the application and exercise ALL features:" - println "- Load different asset types (textures, models, sounds)" - println "- Use UI components" - println "- Trigger network operations" - println "- Test different game states" - println "Then close the application normally." - } - doLast { - println "Tracing complete! Generated/updated files:" - fileTree("${project.projectDir}/trace").each { file -> - if (file.isFile()) { - println " - ${file.name} (${file.length()} bytes)" - } - } - } -} - - -task buildNativeExecutable { - group = 'graalvm' - description = 'Build native executable for current platform' - dependsOn 'nativeCompile' - doLast { - def buildDirNative = "${project.buildDir}/native/nativeCompile" - def executableName = graalvmNative.binaries.main.imageName.get() - if (org.gradle.internal.os.OperatingSystem.current().isWindows()) { - executableName += '.exe' - } - - def osName = getOsName() - def osArch = System.getProperty("os.arch").toLowerCase() - def buildName = "${rootProject.name}-${project.version}-${osName}-${osArch}"; - - def distDir = file("${rootProject.projectDir}/dist/${buildName}.buildNative") - distDir.mkdirs() - - copy { - from buildDirNative - into distDir - exclude '**/reports/**' - exclude '**/*.report' - exclude '**/*.dmp' - exclude '**/*.log' - } - - println "Native executable built: ${buildDirNative}/${executableName}" - println "All native build files copied to: ${distDir}" - } -} - - - - def jpackage(installerType, distDir){ - def os = org.gradle.internal.os.OperatingSystem.current() - - - - def jarFile = shadowJar.archiveFile.get().asFile - def jarDir = jarFile.parentFile - def mainClass = application.mainClass.get() - - - def shadowJarFile = shadowJar.archiveFile.get().asFile - - def commandLine = [ - 'jpackage', - '--input', jarDir.absolutePath, - '--main-jar', jarFile.name, - '--main-class', mainClass, - '--name', rootProject.name, - '--app-version', project.version, - '--type', installerType, - '--dest', distDir, - '--java-options', project.findProperty('javaOptions') ?: '', - '--copyright', project.findProperty('copyright') - ] - - // Optional: platform-specific flags - if (os.isWindows()) { - commandLine += ['--win-menu', '--win-shortcut'] - } else if (os.isMacOsX()) { - commandLine += ['--icon', "${rootProject.projectDir}/icon.icns"] - } else if (os.isLinux()) { - commandLine += ['--icon', "${rootProject.projectDir}/icon.png"] - } - return commandLine -} - - - -task buildInstaller(type: DefaultTask) { - group = 'distribution' - description = 'Create native installer using jpackage' - dependsOn shadowJar - - def os = org.gradle.internal.os.OperatingSystem.current() - def osName = getOsName() - def osArch = System.getProperty("os.arch").toLowerCase() - def buildName = "${rootProject.name}-${project.version}-${osName}-${osArch}-SETUP"; - def distDir = file("${rootProject.projectDir}/dist/${buildName}.setup") - - - doLast { - if (distDir.exists()) { - println "Deleting existing output directory: ${distDir}" - delete distDir - } - - if(os.isWindows()){ - exec { - commandLine jpackage('exe', distDir) - } - } else if(os.isMacOsX()){ - exec { - commandLine jpackage('dmg', distDir) - } - } else if(os.isLinux()){ - exec { - commandLine jpackage('deb', distDir) - } - exec { - commandLine jpackage('rpm', distDir) - } - } - } -} + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} \ No newline at end of file diff --git a/app/src/main/java/org/ngengine/demo/adc/AdcDemo.java b/app/src/main/java/org/ngengine/app/Main.java similarity index 68% rename from app/src/main/java/org/ngengine/demo/adc/AdcDemo.java rename to app/src/main/java/org/ngengine/app/Main.java index 6b6baf5..77a00fb 100644 --- a/app/src/main/java/org/ngengine/demo/adc/AdcDemo.java +++ b/app/src/main/java/org/ngengine/app/Main.java @@ -1,17 +1,26 @@ -package org.ngengine.demo.adc; +package org.ngengine.app; import org.ngengine.NGEApplication; +import org.ngengine.NGEApplication.NGEAppRunner; import org.ngengine.ads.ImmersiveAdComponent; import org.ngengine.components.ComponentManager; +import org.ngengine.demo.adc.CharacterComponent; +import org.ngengine.demo.adc.HudComponent; +import org.ngengine.demo.adc.LoadingScreenComponent; +import org.ngengine.demo.adc.MapComponent; +import org.ngengine.demo.adc.PhysicsComponent; +import org.ngengine.demo.adc.PostprocessingComponent; +import org.ngengine.demo.adc.SoundComponent; import org.ngengine.gui.win.NWindowManagerComponent; +import org.ngengine.nostr4j.keypair.NostrPublicKey; import com.jme3.system.AppSettings; -public class AdcDemo { +public class Main { - public static void main(String arg[]){ + public static NGEAppRunner main(String arg[]){ AppSettings settings = new AppSettings(true); settings.setRenderer(AppSettings.LWJGL_OPENGL32); settings.setWidth(1280); @@ -24,13 +33,14 @@ public class AdcDemo { settings.setGraphicsDebug(false); settings.setX11PlatformPreferred(true); settings.setTitle("Nostr Game Engine Demo"); - - + settings.setEmulateMouse(true); + settings.setEmulateKeyboard(true); + + NostrPublicKey appId = NostrPublicKey.fromBech32("npub1tc32kq8hr992tzvt09rwuc5wk6aa08pquqawasv20acjp4umny5q6axr4t"); - Runnable appBuilder = NGEApplication.createApp(settings, app -> { + NGEAppRunner appRunner = NGEApplication.createApp(appId, settings, app -> { ImmersiveAdComponent dep = app.enableAds(); - ComponentManager mng = app.getComponentManager(); mng.addAndEnableComponent(new LoadingScreenComponent(), null, new Object[] { NWindowManagerComponent.class }); @@ -50,6 +60,6 @@ public class AdcDemo { }); - appBuilder.run(); + return appRunner; } } diff --git a/app/src/main/java/org/ngengine/demo/adc/MapComponent.java b/app/src/main/java/org/ngengine/demo/adc/MapComponent.java index 2e6e55e..5dbd7f0 100644 --- a/app/src/main/java/org/ngengine/demo/adc/MapComponent.java +++ b/app/src/main/java/org/ngengine/demo/adc/MapComponent.java @@ -51,6 +51,7 @@ public class MapComponent implements Component, AsyncAssetLoadingFragmen if(map==null){ System.out.println("AdCity: Loading map from assets..."); map = assetManager.loadModel("adc/city/city.gltf"); + // map = new Node(); PhysicsComponent physics = mng.getComponent(PhysicsComponent.class); map.depthFirstTraversal(sx->{ Object worldGuard = sx.getUserData("nge.worldguard"); diff --git a/gradle.properties b/gradle.properties index 1442e20..135e75e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,20 @@ +# Gradle settings org.gradle.configuration-cache=false +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true -ngeVersion=0.1.0-SNAPSHOT +# NGE versions +ngePlatformVersion=0.1.3 +ngeVersion=0.1 + +# Build targets +withDesktopPlatform=true +withAndroidPlatform=false + +# Project settings version=1.0.0 +versionCode=1 projectName=adcity -mainClass=org.ngengine.demo.adc.AdcDemo +applicationId=org.ngengine.demo.AdcDemo copyright=Copyright (c) 2025 -javaOptions= \ No newline at end of file +javaOptions= diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f85790b..466d564 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,9 +2,26 @@ # https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format [versions] +androidxJunit = "1.3.0" +appcompat = "1.7.1" +constraintlayout = "2.2.1" +espressoCore = "3.7.0" guava = "33.3.1-jre" junit = "4.13.2" +junitVersion = "4.13.3-SNAPSHOT" +material = "1.12.0" +ngePlatformAndroid = "0.1.0-SNAPSHOT" +navigationFragment = "2.6.0" +navigationUi = "2.6.0" [libraries] +appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } +constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } +espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" } +ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidxJunit" } guava = { module = "com.google.guava:guava", version.ref = "guava" } junit = { module = "junit:junit", version.ref = "junit" } +junit-v4133snapshot = { module = "junit:junit", version.ref = "junitVersion" } +material = { module = "com.google.android.material:material", version.ref = "material" } +navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } +navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } diff --git a/local.properties b/local.properties new file mode 100644 index 0000000..27d9bd5 --- /dev/null +++ b/local.properties @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Mon Sep 01 09:13:24 CEST 2025 +sdk.dir=/home/riccardobl/Android/Sdk diff --git a/platform-android/build.gradle b/platform-android/build.gradle new file mode 100644 index 0000000..b9f5cb3 --- /dev/null +++ b/platform-android/build.gradle @@ -0,0 +1,58 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 36 + namespace "${project.applicationId}" + + defaultConfig { + applicationId "${project.applicationId}" + minSdk 33 + targetSdk 33 + versionCode project.versionCode.toInteger() + versionName project.version + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + packagingOptions { + jniLibs { + useLegacyPackaging = true + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + buildFeatures { + viewBinding true + } + +} + + +dependencies { + implementation libs.appcompat + implementation libs.material + implementation libs.constraintlayout + implementation libs.navigation.fragment + implementation libs.navigation.ui + testImplementation libs.junit.v4133snapshot + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core + + + implementation project(':app') + implementation "org.ngengine:nge-platform-android:${ngePlatformVersion}" + implementation "org.ngengine:nge-app:${ngeVersion}" + implementation "org.ngengine:jme3-android:${ngeVersion}" + implementation "org.ngengine:jme3-android-native:${ngeVersion}" + +} diff --git a/platform-android/src/androidTest/java/.gitkeep b/platform-android/src/androidTest/java/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/platform-android/src/main/AndroidManifest.xml b/platform-android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5a62138 --- /dev/null +++ b/platform-android/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/platform-android/src/main/java/org/ngengine/platform/AndroidLauncherActivity.java b/platform-android/src/main/java/org/ngengine/platform/AndroidLauncherActivity.java new file mode 100644 index 0000000..d0a8cfd --- /dev/null +++ b/platform-android/src/main/java/org/ngengine/platform/AndroidLauncherActivity.java @@ -0,0 +1,36 @@ +package org.ngengine.platform; + +import android.os.Bundle; + +import com.google.android.material.snackbar.Snackbar; + +import androidx.appcompat.app.AppCompatActivity; + +import android.view.View; + +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.navigation.ui.AppBarConfiguration; +import androidx.navigation.ui.NavigationUI; + + +import org.ngengine.demo.AdcDemo.R; + +public class AndroidLauncherActivity extends AppCompatActivity { + + private AndroidLauncherFragment launcherFragment; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_android_launcher); + + launcherFragment = new AndroidLauncherFragment(); + getSupportFragmentManager() + .beginTransaction() + .add(R.id.fragment_container, launcherFragment) + .commit(); + } + + +} \ No newline at end of file diff --git a/platform-android/src/main/java/org/ngengine/platform/AndroidLauncherFragment.java b/platform-android/src/main/java/org/ngengine/platform/AndroidLauncherFragment.java new file mode 100644 index 0000000..cfebea4 --- /dev/null +++ b/platform-android/src/main/java/org/ngengine/platform/AndroidLauncherFragment.java @@ -0,0 +1,308 @@ + +package org.ngengine.platform; + +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.fragment.app.Fragment; + +import com.jme3.audio.AudioRenderer; +import com.jme3.input.JoyInput; +import com.jme3.input.android.AndroidSensorJoyInput; + +import com.jme3.system.AppSettings; +import com.jme3.system.SystemListener; +import com.jme3.system.android.JmeAndroidSystem; +import com.jme3.system.android.OGLESContext; +import com.jme3.util.AndroidLogHandler; +import com.jme3.util.AndroidNativeBufferAllocator; +import com.jme3.util.BufferAllocatorFactory; + +import android.app.AlertDialog; +import org.ngengine.NGEApplication.NGEAppRunner; + +import org.ngengine.NGEApplication; +import org.ngengine.app.Main; +import org.ngengine.platform.android.AndroidThreadedPlatform; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + + +public class AndroidLauncherFragment extends Fragment implements + SystemListener { + private static final Logger logger = Logger.getLogger(AndroidLauncherFragment.class.getName()); + + protected GLSurfaceView view = null; + + private NGEApplication app = null; + private boolean finishOnAppStop = true; + + + @Override + public void onAttach(Context context) { + super.onAttach(context); + } + + public void setCloseActivityOnAppStop(boolean close){ + this.finishOnAppStop = close; + } + + /** + * onCreate is called once for this Fragment instance. + * Note: AppSettings configuration has been removed. Configure settings in your app. + */ + @Override + @SuppressWarnings("unchecked") + public void onCreate(Bundle savedInstanceState) { + initializeLogHandler(); + logger.fine("onCreate"); + super.onCreate(savedInstanceState); + + System.setProperty( BufferAllocatorFactory.PROPERTY_BUFFER_ALLOCATOR_IMPLEMENTATION, AndroidNativeBufferAllocator.class.getName()); + NGEPlatform.set(new AndroidThreadedPlatform(this.getContext())); + NGEAppRunner runner = Main.main(new String[0]); + app = runner.app(); + + AppSettings settings = app.getSettings(); + settings.setAudioRenderer(AppSettings.ANDROID_OPENAL_SOFT); + + app.start(); + + + OGLESContext ctx = (OGLESContext) app.getJme3App().getContext(); + ctx.setSystemListener(this); + } + + /** + * Create the GLSurfaceView and return it as the Fragment's view. + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + logger.fine("onCreateView"); + view = ((OGLESContext) app.getJme3App().getContext()).createView(requireActivity()); + JmeAndroidSystem.setView(view); + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + logger.fine("onActivityCreated"); + super.onActivityCreated(savedInstanceState); + } + + @Override + public void onStart() { + logger.fine("onStart"); + super.onStart(); + } + + /** + * When the Fragment resumes, call gainFocus() in the jME application. + */ + @Override + public void onResume() { + logger.fine("onResume"); + super.onResume(); + gainFocus(); + } + + /** + * When the Fragment pauses, call loseFocus() in the jME application. + */ + @Override + public void onPause() { + logger.fine("onPause"); + loseFocus(); + super.onPause(); + } + + @Override + public void onStop() { + logger.fine("onStop"); + super.onStop(); + } + + /** + * Clear references to the GLSurfaceView. + */ + @Override + public void onDestroyView() { + logger.fine("onDestroyView"); + if (view != null) { + if (view.getParent() instanceof ViewGroup) { + ((ViewGroup) view.getParent()).removeView(view); + } + } + view = null; + JmeAndroidSystem.setView(null); + super.onDestroyView(); + } + + /** + * Called by the system when the application is being destroyed. + */ + @Override + public void onDestroy() { + logger.fine("onDestroy"); + if (app != null) { + app.stop(); + } + app = null; + super.onDestroy(); + } + + /** + * Called when an error has occurred. Shows an error message and logs the exception. + */ + @Override + public void handleError(final String errorMsg, final Throwable t) { + String stackTrace = ""; + String title = "Error"; + + if (t != null) { + // Convert exception to string + StringWriter sw = new StringWriter(100); + t.printStackTrace(new PrintWriter(sw)); + stackTrace = sw.toString(); + title = t.toString(); + } + + final String finalTitle = title; + final String finalMsg = (errorMsg != null ? errorMsg : "Uncaught Exception") + + "\n" + stackTrace; + + logger.log(Level.SEVERE, finalMsg); + + requireActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()); + builder.setTitle(finalTitle); + builder.setMessage(finalMsg); + + AlertDialog dialog = builder.create(); + dialog.show(); + } + }); + } + + + + + /** + * Removes the standard Android log handler due to an issue with not logging + * entries lower than INFO level and adds a handler that produces + * JME formatted log messages. + */ + protected void initializeLogHandler() { + Logger log = LogManager.getLogManager().getLogger(""); + for (Handler handler : log.getHandlers()) { + if (log.getLevel() != null && log.getLevel().intValue() <= Level.FINE.intValue()) { + Log.v("AndroidHarness", "Removing Handler class: " + handler.getClass().getName()); + } + log.removeHandler(handler); + } + Handler handler = new AndroidLogHandler(); + log.addHandler(handler); + handler.setLevel(Level.ALL); + } + + @Override + public void initialize() { + app.getJme3App().initialize(); + + } + + @Override + public void reshape(int width, int height) { + app.getJme3App().reshape(width, height); + } + + @Override + public void rescale(float x, float y) { + app.getJme3App().rescale(x, y); + } + + @Override + public void update() { + app.getJme3App().update(); + } + + @Override + public void requestClose(boolean esc) { + app.getJme3App().requestClose(esc); + } + + @Override + public void destroy() { + if (app != null) { + app.getJme3App().destroy(); + } + if (finishOnAppStop) { + requireActivity().finish(); + } + } + + @Override + public void gainFocus() { + logger.fine("gainFocus"); + if (view != null) { + view.onResume(); + } + + if (app != null) { + // resume the audio + AudioRenderer audioRenderer = app.getJme3App().getAudioRenderer(); + if (audioRenderer != null) { + audioRenderer.resumeAll(); + } + // resume the sensors (aka joysticks) + if (app.getJme3App().getContext() != null) { + JoyInput joyInput = app.getJme3App().getContext().getJoyInput(); + if (joyInput instanceof AndroidSensorJoyInput) { + ((AndroidSensorJoyInput) joyInput).resumeSensors(); + } + } + app.getJme3App().gainFocus(); + } + } + + @Override + public void loseFocus() { + logger.fine("loseFocus"); + if (app != null) { + app.getJme3App().loseFocus(); + } + + if (view != null) { + view.onPause(); + } + + if (app != null) { + // pause the audio + AudioRenderer audioRenderer = app.getJme3App().getAudioRenderer(); + if (audioRenderer != null) { + audioRenderer.pauseAll(); + } + // pause the sensors (aka joysticks) + if (app.getJme3App().getContext() != null) { + JoyInput joyInput = app.getJme3App().getContext().getJoyInput(); + if (joyInput instanceof AndroidSensorJoyInput) { + ((AndroidSensorJoyInput) joyInput).pauseSensors(); + } + } + } + } + + +} \ No newline at end of file diff --git a/platform-android/src/main/res/drawable-v24/ic_launcher_foreground.xml b/platform-android/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..7706ab9 --- /dev/null +++ b/platform-android/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + diff --git a/platform-android/src/main/res/drawable/ic_launcher_background.xml b/platform-android/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/platform-android/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platform-android/src/main/res/layout/activity_android_launcher.xml b/platform-android/src/main/res/layout/activity_android_launcher.xml new file mode 100644 index 0000000..25b2cb1 --- /dev/null +++ b/platform-android/src/main/res/layout/activity_android_launcher.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/platform-android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/platform-android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6b78462 --- /dev/null +++ b/platform-android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/platform-android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/platform-android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..6b78462 --- /dev/null +++ b/platform-android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/platform-android/src/main/res/mipmap-hdpi/ic_launcher.webp b/platform-android/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..3e37e739a315f39c55e22bd7bc1a12748d6f8ef3 GIT binary patch literal 2513 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 GX8I2WfAhxx literal 0 HcmV?d00001 diff --git a/platform-android/src/main/res/mipmap-xhdpi/ic_launcher.webp b/platform-android/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..13410d77ac289fd08f3113d264b155ae44d9fdfb GIT binary patch literal 3477 zcmZWr`A<|=6n=mma4;|+o60gQiijY)po}mq!@z(bn{2}%44Z7S4%XS$>-4_u{*6`#M2M5W%F!Gat6?haT<7GbgUV=<0p=5fndph@S|BOhSOZ?V^XcXbL19 z;!Y=f8~I%O)(|kjh2{^6U2_V$0+O6s4_*wPRwcT{@AO(g4=>(cgEB#ei$DxazyiiW ztm_}^zCymzSnRddcEgB<7%dG&`Ou8y0Hg=xv}}>0U7W^$z%DIGTL(c zHT@YFdmzk)8<=X(;%9VlvFIpwYA06?ZWe!}5xT>O-Z6(5G!27zlHa}_tw%eO9S*w; zl4#_fY9@A4#|{us-y8kJ1uL=?fVX}aocDvY3p?tR=^7MW242j~VC5Y*S zQlINOPQU4}OzehoKtj6JYT{<><7#Ueodwc6#c~3VuC0z#GoxEqKqtis?wRLn0QbKp zocU6KIY?b1h>ZtiL$bIokY-FV-8lzit`QW^cgu4?F#$X(6Ep?!x-~T^rYY53x=T!# zFN}evxd3s-irNYlHmpJs7uY=0Jpq-*ENiUG+`oO0K1g>LNC$gPu*jk%L0)b!bgm1; zYOmCS1gfF9gv%dff%FbI!4Iy0d~vQb>E?WmTdFDxi?0CpZ-TgJ|6{eOJbs}NAXRCY zHb|3rbAm;KH_m{3Yd(0pbPx_dhE@*=spYx;{rt_1n@m7Q2ds9k2;_zGnFXhR{)=60itX*IP_ms{tc+?z7L>S&V)g1n|{!V6oP}oX<5r|KcoE zPTccX8F_IgNJncjOfRd*84r3$~&p%KCsS>l`2_5Wq(|(l>D+BcYx%Porn|VS8tj+LdKaUo`dNU`$&>WiL z6tkup`nMa=6OlIay-(@}dBxcwCs;UOnOhi?ImNuHD>(pEfQE?sm$`$pyer zJvM`!-2f_Zs34ff64yEk(UA^I9hU@g8Xj@iBZoFluc$*F!|h`^dmy=4)8M6?Ffjem z5boN45aCv52mTR#?vJnj^&EFX9YJ1$K+8_G7UOF73kWKo9vV-s;#~lEcqsuU2x~p- zyN_hT!|onq?!dCBtxz-U8bO9`!3b0fwx-mvRshcuFms}M{f$_$!91sjdx~mipjVkr z0YnE2_X>m`EbvtFwa4$W%4}R9TvWQJf${c30=XxgJ;^+o&fO1>v2p7noM3^GUt4DQ zQN_A*6xPV@TpmEP?i7QIcRKC43`I~VYNbi{B>qm%v1%oo?c?9?ZWu=4n0!BjH2 zx{kXy##B6}v`uLb2P5D?x$-qA~_67s^@E z?fD4|k@sAbOsS)NoT1fDi4VL7lAhE7=DAb-l*2-E24KA%&68le?atRw>_PT1w>cQ% ziys3)kZ!?fjxld>m#4yF_>bR`IW8!eYqN1VhUtbdryUNXJ!gLwuNwz5%d9jE9pv5u z{4HiKGjnlZ1`4PT8VlxT#bKWp4OX`*F93E1E1zVEetzv741oJp1L6Hx7xmZ0PsDJd+oXfN}#TrC)asHaOs42-ZCDUb?|mHaY@0wt9&z52B)Bomc@Wg%-;3n zEDxZdUd8+J4{CI-I4f3+d>#1y!YX^&ySH4%;9u5CbQ?zdv)5ZTkHy{%)xg62{H8wn z<0b9sX&G^K-e;_dU`142$Pu=t4zp9tQ9fNHHqQw+XO>5ZhF}n?*u{FdQrTv=FR@90 zSl=@kEkO#HwDAxqYl`Q28lt-O1WcfL<8xj{zn%fP57mh?CeLe!#eNm=erRA0cQ12$ zcya)@!3wTmZ>2#3Abndvsmwvp6>nJ%(?>AMLO>%f(1Kbe+RB6kDG9pCQv_@a{plOA Lm=B`@G|l)AlbX$% literal 0 HcmV?d00001 diff --git a/platform-android/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/platform-android/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..554b15e12d958e2d7ef973c2a1acf64300131d1b GIT binary patch literal 7138 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 Smm9R4KhE(2#_>P$4F3TvT{hbQ literal 0 HcmV?d00001 diff --git a/platform-android/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/platform-android/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..fe6bf91d5a2bf828c21582e692494f4c7b451fc0 GIT binary patch literal 5311 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 Q2Zt#In88{6lTv5+KP4QRB>(^b literal 0 HcmV?d00001 diff --git a/platform-android/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/platform-android/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..f8baf99e963e6d8c84ef484f07db86dd545637e0 GIT binary patch literal 10718 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 literal 0 HcmV?d00001 diff --git a/platform-android/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/platform-android/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..698b15c82afd54507413911d5de12d19e42a6b55 GIT binary patch literal 6990 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 KgY4En{r>>gx}(tm literal 0 HcmV?d00001 diff --git a/platform-android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/platform-android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f3ce06c8004b5fff9ce18ab887962fef94fa715 GIT binary patch literal 14156 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 + if (file.isFile()) { + println " - ${file.name} (${file.length()} bytes)" + } + } + } +} + + +task buildNativeExecutable { + group = 'graalvm' + description = 'Build native executable for current platform' + dependsOn 'nativeCompile' + doLast { + def buildDirNative = "${project.buildDir}/native/nativeCompile" + def executableName = graalvmNative.binaries.main.imageName.get() + if (org.gradle.internal.os.OperatingSystem.current().isWindows()) { + executableName += '.exe' + } + + def osName = getOsName() + def osArch = System.getProperty("os.arch").toLowerCase() + def buildName = "${rootProject.name}-${project.version}-${osName}-${osArch}"; + + def distDir = file("${rootProject.projectDir}/dist/${buildName}.buildNative") + distDir.mkdirs() + + copy { + from buildDirNative + into distDir + exclude '**/reports/**' + exclude '**/*.report' + exclude '**/*.dmp' + exclude '**/*.log' + } + + println "Native executable built: ${buildDirNative}/${executableName}" + println "All native build files copied to: ${distDir}" + } +} + + + + def jpackage(installerType, distDir){ + def os = org.gradle.internal.os.OperatingSystem.current() + + + + def jarFile = shadowJar.archiveFile.get().asFile + def jarDir = jarFile.parentFile + def mainClass = application.mainClass.get() + + + def shadowJarFile = shadowJar.archiveFile.get().asFile + + def commandLine = [ + 'jpackage', + '--input', jarDir.absolutePath, + '--main-jar', jarFile.name, + '--main-class', mainClass, + '--name', rootProject.name, + '--app-version', project.version, + '--type', installerType, + '--dest', distDir, + '--java-options', project.findProperty('javaOptions') ?: '', + '--copyright', project.findProperty('copyright') + ] + + // Optional: platform-specific flags + if (os.isWindows()) { + commandLine += ['--win-menu', '--win-shortcut'] + } else if (os.isMacOsX()) { + commandLine += ['--icon', "${rootProject.projectDir}/icon.icns"] + } else if (os.isLinux()) { + commandLine += ['--icon', "${rootProject.projectDir}/icon.png"] + } + return commandLine +} + + + +task buildInstaller(type: DefaultTask) { + group = 'distribution' + description = 'Create native installer using jpackage' + dependsOn shadowJar + + def os = org.gradle.internal.os.OperatingSystem.current() + def osName = getOsName() + def osArch = System.getProperty("os.arch").toLowerCase() + def buildName = "${rootProject.name}-${project.version}-${osName}-${osArch}-SETUP"; + def distDir = file("${rootProject.projectDir}/dist/${buildName}.setup") + + + doLast { + if (distDir.exists()) { + println "Deleting existing output directory: ${distDir}" + delete distDir + } + + if(os.isWindows()){ + exec { + commandLine jpackage('exe', distDir) + } + } else if(os.isMacOsX()){ + exec { + commandLine jpackage('dmg', distDir) + } + } else if(os.isLinux()){ + exec { + commandLine jpackage('deb', distDir) + } + exec { + commandLine jpackage('rpm', distDir) + } + } + } +} + + + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} \ No newline at end of file diff --git a/app/graal.initialize-at-runtime.conf b/platform-desktop/graal.initialize-at-runtime.conf similarity index 100% rename from app/graal.initialize-at-runtime.conf rename to platform-desktop/graal.initialize-at-runtime.conf diff --git a/platform-desktop/src/main/java/org/ngengine/platform/.gitkeep b/platform-desktop/src/main/java/org/ngengine/platform/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/platform-desktop/src/main/java/org/ngengine/platform/DesktopLauncher.java b/platform-desktop/src/main/java/org/ngengine/platform/DesktopLauncher.java new file mode 100644 index 0000000..05bd0cd --- /dev/null +++ b/platform-desktop/src/main/java/org/ngengine/platform/DesktopLauncher.java @@ -0,0 +1,14 @@ +package org.ngengine.platform; + +import org.ngengine.app.Main; +import org.ngengine.platform.jvm.JVMAsyncPlatform; +import org.ngengine.NGEApplication.NGEAppRunner; + +public class DesktopLauncher { + public static void main(String[] args) { + NGEPlatform.set(new JVMAsyncPlatform()); + NGEAppRunner runner = Main.main(args); + runner.start(); + } + +} diff --git a/platform-desktop/src/main/resources/.gitkeep b/platform-desktop/src/main/resources/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/platform-desktop/src/test/java/.gitkeep b/platform-desktop/src/test/java/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/trace/jni-config.json b/platform-desktop/trace/jni-config.json similarity index 100% rename from app/trace/jni-config.json rename to platform-desktop/trace/jni-config.json diff --git a/app/trace/predefined-classes-config.json b/platform-desktop/trace/predefined-classes-config.json similarity index 100% rename from app/trace/predefined-classes-config.json rename to platform-desktop/trace/predefined-classes-config.json diff --git a/app/trace/proxy-config.json b/platform-desktop/trace/proxy-config.json similarity index 100% rename from app/trace/proxy-config.json rename to platform-desktop/trace/proxy-config.json diff --git a/app/trace/reachability-metadata.json b/platform-desktop/trace/reachability-metadata.json similarity index 100% rename from app/trace/reachability-metadata.json rename to platform-desktop/trace/reachability-metadata.json diff --git a/app/trace/reflect-config.json b/platform-desktop/trace/reflect-config.json similarity index 100% rename from app/trace/reflect-config.json rename to platform-desktop/trace/reflect-config.json diff --git a/app/trace/resource-config.json b/platform-desktop/trace/resource-config.json similarity index 100% rename from app/trace/resource-config.json rename to platform-desktop/trace/resource-config.json diff --git a/app/trace/serialization-config.json b/platform-desktop/trace/serialization-config.json similarity index 100% rename from app/trace/serialization-config.json rename to platform-desktop/trace/serialization-config.json diff --git a/settings.gradle b/settings.gradle index 8e986b1..2903720 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,44 @@ -plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0' + +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0' + id("com.android.application") version '8.12.2' apply false + id("com.android.library") version '8.12.2' apply false + id("org.jetbrains.kotlin.android") version "2.1.20" apply false + } } + + + + + +dependencyResolutionManagement { + repositories { + mavenLocal() + mavenCentral() + google() + maven { + url = uri("https://central.sonatype.com/repository/maven-snapshots") + } + } +} + + + rootProject.name = settings.projectName include('app') + +if(withDesktopPlatform) { + include('platform-desktop') +} + +if (withAndroidPlatform) { + include('platform-android') +} \ No newline at end of file