From 127d768dbe962113d70feee841c4d9bdafb86852 Mon Sep 17 00:00:00 2001 From: zmertens Date: Mon, 29 Jan 2024 06:45:33 -0700 Subject: [PATCH] Update Android example to use SDL3 --- .../example_android_opengl3/CMakeLists.txt | 258 +++++++++++++++--- examples/example_android_opengl3/README.md | 16 ++ .../android/.gitignore | 8 + .../android/app/build.gradle | 36 ++- .../android/app/src/main/AndroidManifest.xml | 105 ++++++- .../android/build.gradle | 2 +- 6 files changed, 367 insertions(+), 58 deletions(-) create mode 100644 examples/example_android_opengl3/README.md diff --git a/examples/example_android_opengl3/CMakeLists.txt b/examples/example_android_opengl3/CMakeLists.txt index 63531f4dc48b..afecd912fe56 100644 --- a/examples/example_android_opengl3/CMakeLists.txt +++ b/examples/example_android_opengl3/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.2) project(ImGuiExample) @@ -6,35 +6,227 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -add_library(${CMAKE_PROJECT_NAME} SHARED - ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../imgui.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../imgui_demo.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../imgui_draw.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../imgui_tables.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../imgui_widgets.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../backends/imgui_impl_android.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../backends/imgui_impl_opengl3.cpp - ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c -) - -set(CMAKE_SHARED_LINKER_FLAGS - "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate" -) - -target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE - IMGUI_IMPL_OPENGL_ES3 -) - -target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../.. - ${CMAKE_CURRENT_SOURCE_DIR}/../../backends - ${ANDROID_NDK}/sources/android/native_app_glue -) - -target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE - android - EGL - GLESv3 - log -) +option(CONFIGURE_WITH_SDL3 "Configure with SDL3: ON/OFF" OFF) + +# Android Project +if(CONFIGURE_WITH_SDL3) + message(INFO ": Configuring Android example with SDL3") + if(NOT DEFINED ENV{ANDROID_HOME}) + message(FATAL_ERROR "Set ANDROID_HOME to path of Android Sdk.") + endif() + + if(NOT DEFINED ENV{ANDROID_NDK_HOME}) + message(FATAL_ERROR "Set ANDROID_NDK_HOME to path of Android Ndk.") + endif() + + # Download SDL3 + set(SDL_DIR_NAME "SDL-main") + set(DOWNLOADED_SDL_DIR "${CMAKE_BINARY_DIR}/${SDL_DIR_NAME}") + set(SDL_MAIN_BRANCH_ZIP_URL "https://github.com/libsdl-org/SDL/archive/main.zip") + # Package name should match the same in Gradle build files + set(IMGUI_PKG_NAME "imgui.example.android") + set(IMGUI_ACTIVITY "ImguiActivity") + + file(DOWNLOAD ${SDL_MAIN_BRANCH_ZIP_URL} SHOW_PROGRESS ${DOWNLOADED_SDL_DIR}.zip STATUS DOWNLOAD_STATUS) + file(ARCHIVE_EXTRACT INPUT "${DOWNLOADED_SDL_DIR}.zip") + + list(GET DOWNLOAD_STATUS 0 STATUS_CODE) + list(GET DOWNLOAD_STATUS 1 ERROR_MESSAGE) + + # Check if download was successful. + if(${STATUS_CODE} EQUAL 0) + message(INFO ": ${SDL_DIR_NAME}.zip download completed successfully!") + else() + message(FATAL_ERROR "Error occurred during download: ${ERROR_MESSAGE}") + endif() + + set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..) + set(SDL3_OPENGL3_DIR "${IMGUI_DIR}/examples/example_sdl3_opengl3") + set(ANDROID_OPENGL3_DIR "${IMGUI_DIR}/examples/example_android_opengl3") + set(IMGUI_BACKENDS_DIR "${IMGUI_DIR}/backends") + set(SDL3_MAIN_CPP "main_sdl3.cpp") + + # Copy Gradle files + set(GRADLE_WRAPPER_DOWNLOAD "${DOWNLOADED_SDL_DIR}/android-project/gradlew") + set(GRADLE_BATCH_DOWNLOAD "${DOWNLOADED_SDL_DIR}/android-project/gradlew.bat") + set(GRADLE_WRAPPER_JAR "gradle-wrapper.jar") + set(GRADLE_WRAPPER_DIR "${ANDROID_OPENGL3_DIR}/android/gradle/wrapper") + set(GRADLE_WRAPPER_PROPS_FILENAME "${GRADLE_WRAPPER_DIR}/gradle-wrapper.properties") + set(GRADLE_WRAPPER_PROPS_CONTENT "distributionBase=GRADLE_USER_HOME\n" + "distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip\n" + "distributionPath=wrapper/dists\n" + "zipStorePath=wrapper/dists\n" + "zipStoreBase=GRADLE_USER_HOME\n") + # Check if Gradle Wrapper directory is present in repo + if(NOT EXISTS ${GRADLE_WRAPPER_DIR}) + add_custom_command(OUTPUT CMD PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${GRADLE_WRAPPER_DIR}) + message(INFO ": Made Gradle Wrapper dir") + else() + message(INFO ": Found Gradle Wrapper dir") + endif() + file(WRITE ${GRADLE_WRAPPER_PROPS_FILENAME} ${GRADLE_WRAPPER_PROPS_CONTENT}) + + message(INFO ": Copying Gradle wrapper files to Android project") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GRADLE_WRAPPER_DOWNLOAD}" "${ANDROID_OPENGL3_DIR}/android") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GRADLE_BATCH_DOWNLOAD}" "${ANDROID_OPENGL3_DIR}/android") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${DOWNLOADED_SDL_DIR}/android-project/gradle/wrapper/${GRADLE_WRAPPER_JAR}" "${GRADLE_WRAPPER_DIR}") + + # Copy SDL3 Java files + set(SDL_JAVA_SRC_MAIN "${DOWNLOADED_SDL_DIR}/android-project/app/src/main/java") + set(SDL_JAVA_PKG_PATH "org/libsdl/app") + set(ANDROID_JAVA_PATH "${ANDROID_OPENGL3_DIR}/android/app/src/main/java") + set(ANDROID_SDL_JAVA_FILES "${ANDROID_JAVA_PATH}/${SDL_JAVA_PKG}") + if(NOT EXISTS ${ANDROID_SDL_JAVA_FILES}) + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${ANDROID_SDL_JAVA_FILES}) + message(INFO ": Made dir ${ANDROID_SDL_JAVA_FILES}") + else() + message(INFO ": Found dir ${ANDROID_SDL_JAVA_FILES}") + endif() + message(INFO ": Copying SDL3 Java files to Android project") + list(APPEND SDL_JAVA_FILES HIDDevice HIDDeviceBLESteamController HIDDeviceManager HIDDeviceUSB SDL SDLActivity SDLAudioManager SDLControllerManager SDLDummyEdit SDLInputConnection SDLSurface) + foreach(JAVA_FILE ${SDL_JAVA_FILES}) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SDL_JAVA_SRC_MAIN}/${SDL_JAVA_PKG_PATH}/${JAVA_FILE}.java" "${ANDROID_SDL_JAVA_FILES}/${SDL_JAVA_PKG_PATH}/${JAVA_FILE}.java") + endforeach() + + # Copy SDL's AndroidManifest.xml over to the repo, rewriting the previous example's manifest + set(SDL_MANIFEST_FILE ${DOWNLOADED_SDL_DIR}/android-project/app/src/main/AndroidManifest.xml) + set(ANDROID_MANIFEST_PATH ${ANDROID_JAVA_PATH}/..) + message(INFO ": Writing ${ANDROID_MANIFEST_PATH}/AndroidManifest.xml") + file(COPY ${SDL_MANIFEST_FILE} DESTINATION ${ANDROID_MANIFEST_PATH}) + execute_process(COMMAND sed -i -e "s|name=\"SDLActivity\"|name=\"${IMGUI_ACTIVITY}\"|g" "${ANDROID_MANIFEST_PATH}/AndroidManifest.xml") + # Copy Android resource files + set(SDL_ANDROID_RES "${DOWNLOADED_SDL_DIR}/android-project/app/src/main/res") + set(ANDROID_RES_PATH "${ANDROID_OPENGL3_DIR}/android/app/src/main/res") + message(INFO ": Copying Android resources files") + if(NOT EXISTS ${ANDROID_RES_PATH}) + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${ANDROID_RES_PATH}) + file(COPY ${SDL_ANDROID_RES} DESTINATION ${ANDROID_RES_PATH}/..) + message(INFO ": Made dir ${ANDROID_RES_PATH}") + else() + message(INFO ": Found dir ${ANDROID_RES_PATH}") + endif() + execute_process(COMMAND sed -i -e "s|Game|${PROJECT_NAME}|g" "${ANDROID_RES_PATH}/values/strings.xml") + + # Create the Android Activity which extends SDLActivity + set(IMGUI_ACTIVITY_PATH ${ANDROID_JAVA_PATH}/com/example/imgui) + if(NOT EXISTS ${IMGUI_ACTIVITY_PATH}) + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${IMGUI_ACTIVITY_PATH}) + message(INFO ": Made ${IMGUI_ACTIVITY_PATH}") + else() + message(INFO ": Found ${IMGUI_ACTIVITY_PATH}") + endif() + message(INFO ": Writing ${IMGUI_ACTIVITY_PATH}/${IMGUI_ACTIVITY}.java") + set(ACTIVITY_CONTENT "package ${IMGUI_PKG_NAME}\;\n" + "import org.libsdl.app.SDLActivity\;\n" + "public class ${IMGUI_ACTIVITY} extends SDLActivity {}\n") + file(WRITE ${IMGUI_ACTIVITY_PATH}/${IMGUI_ACTIVITY}.java ${ACTIVITY_CONTENT}) + + # Copy/Link SDL3 source files + option(LINK_SDL_FILES "Link SDL Files" OFF) + set(ANDROID_JNI_SDL_DIR "${ANDROID_OPENGL3_DIR}/android/app/jni/SDL") + if(NOT EXISTS ${ANDROID_JNI_SDL_DIR}) + if(LINK_SDL_FILES) + message(INFO ": Using symbolic links for SDL dir") + file(CREATE_LINK "${DOWNLOADED_SDL_DIR}" ${ANDROID_JNI_SDL_DIR} SYMBOLIC) + else() + message(INFO ": Copying SDL source files to Android JNI dir") + file(COPY "${DOWNLOADED_SDL_DIR}/include" DESTINATION "${ANDROID_JNI_SDL_DIR}") + file(COPY "${DOWNLOADED_SDL_DIR}/src" DESTINATION "${ANDROID_JNI_SDL_DIR}") + file(COPY "${DOWNLOADED_SDL_DIR}/Android.mk" DESTINATION "${ANDROID_JNI_SDL_DIR}") + endif() + else() + message(INFO ": Found SDL sources in Android JNI dir") + endif() + + # Copy Imgui files and backends + set(ANDROID_JNI_SRC_DIR "${ANDROID_OPENGL3_DIR}/android/app/jni/src") + list(APPEND IMGUI_FILES imgui.cpp imgui_demo.cpp imgui_draw.cpp imgui_tables.cpp imgui_widgets.cpp imconfig.h imgui_internal.h imgui.h imstb_rectpack.h imstb_textedit.h imstb_truetype.h) + list(APPEND IMGUI_BACKENDS imgui_impl_opengl3.cpp imgui_impl_sdl3.cpp imgui_impl_opengl3_loader.h imgui_impl_opengl3.h imgui_impl_sdl3.h) + if(NOT EXISTS ${ANDROID_JNI_SRC_DIR}) + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${ANDROID_JNI_SRC_DIR}) + message(INFO ": Made Android JNI dir") + else() + message(INFO ": Found Android JNI dir") + endif() + foreach(IMGUI_FILE ${IMGUI_FILES}) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${IMGUI_DIR}/${IMGUI_FILE}" "${ANDROID_JNI_SRC_DIR}/${IMGUI_FILE}") + endforeach() + foreach(IMGUI_BACKEND ${IMGUI_BACKENDS}) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${IMGUI_BACKENDS_DIR}/${IMGUI_BACKEND}" "${ANDROID_JNI_SRC_DIR}/${IMGUI_BACKEND}") + endforeach() + + # Copy the other SDL3/OpenGL3 example into the Android example (It works!) + message(INFO ": Copying example_sdl3_opengl3/main.cpp to Android project and renaming to ${SDL3_MAIN_CPP}") + file(COPY "${SDL3_OPENGL3_DIR}/main.cpp" DESTINATION "${ANDROID_JNI_SRC_DIR}") + file(RENAME ${ANDROID_JNI_SRC_DIR}/main.cpp ${ANDROID_JNI_SRC_DIR}/${SDL3_MAIN_CPP}) + + message(INFO ": Writing src/Android.mk") + # Create an Android.mk file for the Android SDL3 app + set(SRC_ANDROID_MK "LOCAL_PATH := $(call my-dir)\n" + "include $(CLEAR_VARS)\n" + "LOCAL_MODULE := main\n" + "SDL_PATH := ../SDL\n" + "LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include\n" + "LOCAL_CXXFLAGS := -std=c++11 -Wall -DIMGUI_IMPL_OPENGL_ES2\n" + "LOCAL_SRC_FILES := ${SDL3_MAIN_CPP} \\\n" + "imgui_demo.cpp \\\n" + "imgui_draw.cpp \\\n" + "imgui_tables.cpp \\\n" + "imgui_widgets.cpp \\\n" + "imgui.cpp \\\n" + "imgui_impl_sdl3.cpp \\\n" + "imgui_impl_opengl3.cpp \\\n" + "imgui.h \\\n" + "imconfig.h \\\n" + "imgui_internal.h \\\n" + "imstb_rectpack.h \\\n" + "imstb_textedit.h \\\n" + "imstb_truetype.h \\\n" + "imgui_impl_sdl3.h \\\n" + "imgui_impl_opengl3.h \\\n" + "imgui_impl_opengl3_loader.h \n" + "LOCAL_SHARED_LIBRARIES := SDL3\n" + "LOCAL_LDLIBS := -lEGL -lGLESv3 -lOpenSLES -llog -landroid\n" + "include $(BUILD_SHARED_LIBRARY)\n") + file(WRITE ${ANDROID_JNI_SRC_DIR}/Android.mk ${SRC_ANDROID_MK}) + message(INFO ": Writing Android.mk") + set(ANDROID_MK "include $(call all-subdir-makefiles)\n") + file(WRITE ${ANDROID_JNI_SRC_DIR}/../Android.mk ${ANDROID_MK}) + message(INFO ": Writing Application.mk") + set(APPLICATION_MK "APP_ABI := armeabi-v7a arm64-v8a x86 x86_64\nAPP_PLATFORM=android-19\n") + file(WRITE ${ANDROID_JNI_SRC_DIR}/../Application.mk ${APPLICATION_MK}) +else() + add_library(${CMAKE_PROJECT_NAME} SHARED + ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../imgui.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../imgui_demo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../imgui_draw.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../imgui_tables.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../imgui_widgets.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../backends/imgui_impl_android.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../backends/imgui_impl_opengl3.cpp + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c + ) + + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate" + ) + + target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE + IMGUI_IMPL_OPENGL_ES3 + ) + + target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_CURRENT_SOURCE_DIR}/../../backends + ${ANDROID_NDK}/sources/android/native_app_glue + ) + + target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE + android + EGL + GLESv3 + log + ) + +endif() diff --git a/examples/example_android_opengl3/README.md b/examples/example_android_opengl3/README.md new file mode 100644 index 000000000000..5dce2df9fac6 --- /dev/null +++ b/examples/example_android_opengl3/README.md @@ -0,0 +1,16 @@ +# example_android_opengl3 + +## Build without SDL3 using CMake + +## Build with SDL3 using CMake + +This example runs Dear ImGui demo windows with SDL3 on modern Android (targeting Android SDK 34). + +Before running CMake, ensure the `JAVA_HOME``, `ANDROID_HOME``, and `ANDROID_NDK_HOME`` environment variables are set. + +This command will configure the Android files (it doesn't actually build anything): `cmake -S . -B build .. -DCONFIGURE_WITH_SDL3=ON -DLINK_SDL_FILES=OFF` + * Using `-DLINK_SDL_FILES=OFF` will not use symbolic links, instead will copy the SDL sources and includes into the Android JNI directory. + +Once CMake has finished successfully, change into the `android` directory and run `./gradlew installDebug`. + +This should install the demo app on a connected emulator or physical device. diff --git a/examples/example_android_opengl3/android/.gitignore b/examples/example_android_opengl3/android/.gitignore index 3c7a61910b00..148fa05ca319 100644 --- a/examples/example_android_opengl3/android/.gitignore +++ b/examples/example_android_opengl3/android/.gitignore @@ -10,3 +10,11 @@ local.properties # Android Studio puts a Gradle wrapper here, that we don't want: gradle/ gradlew* + +app/jni/src +app/jni/SDL* +app/jni/Android.mk +app/jni/Application.mk +app/src/main/res +app/src/main/java/com +app/src/main/java/org diff --git a/examples/example_android_opengl3/android/app/build.gradle b/examples/example_android_opengl3/android/app/build.gradle index 53181baa2157..ed9debd435b2 100644 --- a/examples/example_android_opengl3/android/app/build.gradle +++ b/examples/example_android_opengl3/android/app/build.gradle @@ -2,17 +2,27 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' android { - compileSdkVersion 33 - buildToolsVersion "33.0.2" - ndkVersion "25.2.9519653" + compileSdkVersion 34 + buildToolsVersion "34.0.0" defaultConfig { applicationId "imgui.example.android" namespace "imgui.example.android" - minSdkVersion 24 - targetSdkVersion 33 + minSdkVersion 19 + targetSdkVersion 34 versionCode 1 versionName "1.0" + externalNativeBuild { + ndkBuild { + arguments "APP_PLATFORM=android-19" + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + } + // cmake { + // path "../../CMakeLists.txt" + // version '3.22.1' + // } + } + ndkVersion "25.2.9519653" } buildTypes { @@ -31,11 +41,19 @@ android { jvmTarget="11" } - externalNativeBuild { - cmake { - path "../../CMakeLists.txt" - version '3.22.1' + if (!project.hasProperty('EXCLUDE_NATIVE_LIBS')) { + sourceSets.main { + jniLibs.srcDir 'libs' + } + externalNativeBuild { + ndkBuild { + path 'jni/Android.mk' + } + // cmake { + // path 'jni/CMakeLists.txt' + // } } + } } repositories { diff --git a/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml b/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml index a87b95b4f8f1..132a35299bd4 100644 --- a/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml +++ b/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml @@ -1,24 +1,99 @@ - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/example_android_opengl3/android/build.gradle b/examples/example_android_opengl3/android/build.gradle index ccd218524cb4..f7d5d8689fbd 100644 --- a/examples/example_android_opengl3/android/build.gradle +++ b/examples/example_android_opengl3/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.4.1' + classpath 'com.android.tools.build:gradle:8.2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }