SDL: cmake: create Android jars + apks for tests

From a4bb4eef730718a8c464d8c72d763fe75ad54725 Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Mon, 27 Mar 2023 12:03:42 +0200
Subject: [PATCH] cmake: create Android jars + apks for tests

---
 .github/workflows/android.yml                 |  17 +-
 CMakeLists.txt                                |  69 ++++-
 cmake/android/FindSdlAndroid.cmake            | 103 +++++++
 cmake/android/FindSdlAndroidBuildTools.cmake  | 115 ++++++++
 cmake/android/FindSdlAndroidPlatform.cmake    | 123 ++++++++
 cmake/android/SdlAndroidFunctions.cmake       | 276 ++++++++++++++++++
 cmake/android/SdlAndroidScript.cmake          |  74 +++++
 docs/README-android.md                        |  76 +++++
 test/CMakeLists.txt                           | 135 +++++++++
 test/android/cmake/AndroidManifest.xml.cmake  |  70 +++++
 .../cmake/SDLEntryTestActivity.java.cmake     | 121 ++++++++
 test/android/cmake/SDLTestActivity.java.cmake |  33 +++
 .../cmake/res/values/strings.xml.cmake        |   5 +
 .../android/cmake/res/xml/shortcuts.xml.cmake |  24 ++
 .../res/drawable/sdl-test_foreground.xml      | 102 +++++++
 test/android/res/layout/arguments_layout.xml  |  26 ++
 .../res/mipmap-anydpi-v26/sdl-test.xml        |   5 +
 .../res/mipmap-anydpi-v26/sdl-test_round.xml  |   5 +
 test/android/res/mipmap-hdpi/sdl-test.png     | Bin 0 -> 3041 bytes
 .../res/mipmap-hdpi/sdl-test_round.png        | Bin 0 -> 5292 bytes
 test/android/res/mipmap-mdpi/sdl-test.png     | Bin 0 -> 1881 bytes
 .../res/mipmap-mdpi/sdl-test_round.png        | Bin 0 -> 3150 bytes
 test/android/res/mipmap-xhdpi/sdl-test.png    | Bin 0 -> 4426 bytes
 .../res/mipmap-xhdpi/sdl-test_round.png       | Bin 0 -> 7789 bytes
 test/android/res/mipmap-xxhdpi/sdl-test.png   | Bin 0 -> 7389 bytes
 .../res/mipmap-xxhdpi/sdl-test_round.png      | Bin 0 -> 13232 bytes
 test/android/res/mipmap-xxxhdpi/sdl-test.png  | Bin 0 -> 11218 bytes
 .../res/mipmap-xxxhdpi/sdl-test_round.png     | Bin 0 -> 19626 bytes
 test/android/res/values/arg_strings.xml       |   7 +
 .../res/values/sdl-test_background.xml        |   4 +
 30 files changed, 1387 insertions(+), 3 deletions(-)
 create mode 100644 cmake/android/FindSdlAndroid.cmake
 create mode 100644 cmake/android/FindSdlAndroidBuildTools.cmake
 create mode 100644 cmake/android/FindSdlAndroidPlatform.cmake
 create mode 100644 cmake/android/SdlAndroidFunctions.cmake
 create mode 100644 cmake/android/SdlAndroidScript.cmake
 create mode 100644 test/android/cmake/AndroidManifest.xml.cmake
 create mode 100644 test/android/cmake/SDLEntryTestActivity.java.cmake
 create mode 100644 test/android/cmake/SDLTestActivity.java.cmake
 create mode 100644 test/android/cmake/res/values/strings.xml.cmake
 create mode 100644 test/android/cmake/res/xml/shortcuts.xml.cmake
 create mode 100644 test/android/res/drawable/sdl-test_foreground.xml
 create mode 100644 test/android/res/layout/arguments_layout.xml
 create mode 100644 test/android/res/mipmap-anydpi-v26/sdl-test.xml
 create mode 100644 test/android/res/mipmap-anydpi-v26/sdl-test_round.xml
 create mode 100644 test/android/res/mipmap-hdpi/sdl-test.png
 create mode 100644 test/android/res/mipmap-hdpi/sdl-test_round.png
 create mode 100644 test/android/res/mipmap-mdpi/sdl-test.png
 create mode 100644 test/android/res/mipmap-mdpi/sdl-test_round.png
 create mode 100644 test/android/res/mipmap-xhdpi/sdl-test.png
 create mode 100644 test/android/res/mipmap-xhdpi/sdl-test_round.png
 create mode 100644 test/android/res/mipmap-xxhdpi/sdl-test.png
 create mode 100644 test/android/res/mipmap-xxhdpi/sdl-test_round.png
 create mode 100644 test/android/res/mipmap-xxxhdpi/sdl-test.png
 create mode 100644 test/android/res/mipmap-xxxhdpi/sdl-test_round.png
 create mode 100644 test/android/res/values/arg_strings.xml
 create mode 100644 test/android/res/values/sdl-test_background.xml

diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 2805ebc2cc78..20a09fe48312 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -16,7 +16,7 @@ jobs:
       matrix:
         platform:
           - { name: Android.mk  }
-          - { name: CMake, cmake: 1, android_abi: "arm64-v8a", android_platform: 23, arch: "aarch64", artifact: "SDL-android-arm64" }
+          - { name: CMake, cmake: 1, android_abi: "arm64-v8a", android_platform: 23, arch: "aarch64", artifact: "SDL-android-arm64", apk-artifact: "SDL-android-apks-arm64" }
 
     steps:
       - uses: actions/checkout@v3
@@ -28,6 +28,11 @@ jobs:
         if: ${{ matrix.platform.name == 'Android.mk' }}
         run: |
           ./build-scripts/androidbuildlibs.sh
+      - uses: actions/setup-java@v3
+        if: ${{ matrix.platform.name == 'CMake' }}
+        with:
+          distribution: 'temurin'
+          java-version: '11'
       - name: Setup (CMake)
         if: ${{ matrix.platform.name == 'CMake' }}
         run: |
@@ -56,6 +61,10 @@ jobs:
         if: ${{ matrix.platform.name == 'CMake' }}
         run: |
           cmake --build build --config Release --parallel --verbose
+      - name: Build test apk's (CMake)
+        if: ${{ matrix.platform.name == 'CMake' }}
+        run: |
+          cmake --build build --config Release --parallel --verbose --target testaudiocapture-apk testcontroller-apk testmultiaudio-apk testsprite-apk
       - name: Install (CMake)
         if: ${{ matrix.platform.name == 'CMake' }}
         run: |
@@ -88,3 +97,9 @@ jobs:
           if-no-files-found: error
           name: ${{ matrix.platform.artifact }}
           path: build/dist/SDL3*
+      - uses: actions/upload-artifact@v3
+        if: ${{ matrix.platform.name == 'CMake' }}
+        with:
+          if-no-files-found: error
+          name: ${{ matrix.platform.apk-artifact }}
+          path: build/test/*.apk
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e420b65c603b..c289ad043217 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -368,9 +368,11 @@ endforeach()
 # Allow some projects to be built conditionally.
 set_option(SDL_DISABLE_INSTALL    "Disable installation of SDL3" ${SDL3_SUBPROJECT})
 cmake_dependent_option(SDL_DISABLE_INSTALL_CPACK "Create binary SDL3 archive using CPack" ${SDL3_SUBPROJECT} "NOT SDL_DISABLE_INSTALL" ON)
-cmake_dependent_option(SDL_DISABLE_INSTALL_MAN "Install man pages for SDL3" ${SDL3_SUBPROJECT} "NOT SDL_DISABLE_INSTALL;NOT SDL_FRAMEWORK" ON)
+cmake_dependent_option(SDL_DISABLE_INSTALL_DOCS "Install docs for SDL3" ${SDL3_SUBPROJECT} "NOT SDL_DISABLE_INSTALL;NOT SDL_FRAMEWORK" ON)
 set_option(SDL_DISABLE_UNINSTALL  "Disable uninstallation of SDL3" OFF)
 
+cmake_dependent_option(SDL_DISABLE_ANDROID_JAR  "Disable creation of SDL3.jar" ${SDL3_SUBPROJECT} "ANDROID" ON)
+
 option_string(SDL_ASSERTIONS "Enable internal sanity checks (auto/disabled/release/enabled/paranoid)" "auto")
 #set_option(SDL_DEPENDENCY_TRACKING "Use gcc -MMD -MT dependency tracking" ON)
 set_option(SDL_ASSEMBLY            "Enable assembly routines" ${SDL_ASSEMBLY_DEFAULT})
@@ -1310,6 +1312,8 @@ endif()
 
 # Platform-specific options and settings
 if(ANDROID)
+  list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/android")
+
   file(GLOB ANDROID_CORE_SOURCES ${SDL3_SOURCE_DIR}/src/core/android/*.c)
   list(APPEND SOURCE_FILES ${ANDROID_CORE_SOURCES} ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
   set_property(SOURCE "${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c" APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-declaration-after-statement")
@@ -1442,6 +1446,50 @@ if(ANDROID)
     set(HAVE_CLOCK_GETTIME 1)
   endif()
 
+  if(NOT SDL_DISABLE_ANDROID_JAR)
+    find_package(Java)
+    find_package(SdlAndroidPlatform MODULE)
+
+    if(Java_FOUND AND SdlAndroidPlatform_FOUND)
+      include(UseJava)
+      set(path_android_jar "${SDL_ANDROID_PLATFORM_ROOT}/android.jar")
+      set(android_java_sources_root "${SDL3_SOURCE_DIR}/android-project/app/src/main/java")
+      file(GLOB SDL_JAVA_SOURCES "${android_java_sources_root}/org/libsdl/app/*.java")
+      set(CMAKE_JAVA_COMPILE_FLAGS "-encoding;utf-8")
+      add_jar(SDL3-jar
+        SOURCES ${SDL_JAVA_SOURCES}
+        INCLUDE_JARS "${path_android_jar}"
+        OUTPUT_NAME "SDL3"
+        VERSION "${SDL3_VERSION}"
+      )
+      set_property(TARGET SDL3-jar PROPERTY OUTPUT "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}.jar")
+      set(javasourcesjar "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}-sources.jar")
+      string(REGEX REPLACE "${android_java_sources_root}/" "" sdl_relative_java_sources "${SDL_JAVA_SOURCES}")
+      add_custom_command(
+        OUTPUT "${javasourcesjar}"
+        COMMAND ${Java_JAR_EXECUTABLE} cf "${javasourcesjar}" ${sdl_relative_java_sources}
+        WORKING_DIRECTORY "${android_java_sources_root}"
+        DEPENDS ${SDL_JAVA_SOURCES}
+      )
+      add_custom_target(SDL3-javasources ALL DEPENDS "${javasourcesjar}")
+      if(NOT SDL_DISABLE_INSTALL_DOCS)
+        set(javadocdir "${SDL3_BINARY_DIR}/docs/javadoc")
+        set(javadocjar "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}-javadoc.jar")
+        set(javadoc_index_html "${javadocdir}/index.html")
+        add_custom_command(
+          OUTPUT "${javadoc_index_html}"
+          COMMAND ${CMAKE_COMMAND} -E rm -rf "${javadocdir}"
+          COMMAND ${Java_JAVADOC_EXECUTABLE} -encoding utf8 -d "${javadocdir}"
+            -classpath "${path_android_jar}"
+            -author -use -version ${SDL_JAVA_SOURCES}
+          DEPENDS ${SDL_JAVA_SOURCES} "${path_android_jar}"
+        )
+        add_custom_target(SDL3-javadoc ALL DEPENDS "${javadoc_index_html}")
+        set_property(TARGET SDL3-javadoc PROPERTY OUTPUT_DIR "${javadocdir}")
+      endif()
+    endif()
+  endif()
+
 elseif(EMSCRIPTEN)
   # Hide noisy warnings that intend to aid mostly during initial stages of porting a new
   # project. Uncomment at will for verbose cross-compiling -I/../ path info.
@@ -3695,11 +3743,28 @@ if(NOT SDL_DISABLE_INSTALL)
     include(CPack)
   endif()
 
-  if(NOT SDL_DISABLE_INSTALL_MAN)
+  if(ANDROID)
+    set(SDL_INSTALL_JAVADIR "${CMAKE_INSTALL_DATAROOTDIR}/java" CACHE PATH "Path where to install java clases + java sources")
+    if(TARGET SDL3-jar)
+      install(FILES "${SDL3_BINARY_DIR}/SDL3.jar"  "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}.jar"
+        DESTINATION "${SDL_INSTALL_JAVADIR}/SDL3")
+    endif()
+    if(TARGET SDL3-javasources)
+      install(FILES  "${SDL3_BINARY_DIR}/SDL3-${SDL3_VERSION}-sources.jar"
+        DESTINATION "${SDL_INSTALL_JAVADIR}/SDL3")
+    endif()
+  endif()
+
+  if(NOT SDL_DISABLE_INSTALL_DOCS)
     SDL_generate_manpages(
       SYMBOL "SDL_Init"
       WIKIHEADERS_PL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build-scripts/wikiheaders.pl"
     )
+    if(TARGET SDL3-javadoc)
+      set(SDL_INSTALL_JAVADOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/javadoc" CACHE PATH "Path where to install SDL3 javadoc")
+      install(DIRECTORY "${SDL3_BINARY_DIR}/docs/javadoc/"
+        DESTINATION "${SDL_INSTALL_JAVADOCDIR}/SDL3")
+    endif()
   endif()
 endif()
 
diff --git a/cmake/android/FindSdlAndroid.cmake b/cmake/android/FindSdlAndroid.cmake
new file mode 100644
index 000000000000..38a2d7a136c0
--- /dev/null
+++ b/cmake/android/FindSdlAndroid.cmake
@@ -0,0 +1,103 @@
+#[=======================================================================[
+
+FindSdlAndroid
+----------------------
+
+Locate various executables that are essential to creating an Android APK archive.
+This find module uses the FindSdlAndroidBuildTools module to locate some Android utils.
+
+
+Imported targets
+^^^^^^^^^^^^^^^^
+
+This module defines the following :prop_tgt:`IMPORTED` target(s):
+
+`` SdlAndroid::aapt2 ``
+   Imported executable for the "android package tool" v2
+
+`` SdlAndroid::apksigner``
+   Imported executable for the APK signer tool
+
+`` SdlAndroid::d8 ``
+   Imported executable for the dex compiler
+
+`` SdlAndroid::zipalign ``
+   Imported executable for the zipalign util
+
+`` SdlAndroid::adb ``
+   Imported executable for the "android debug bridge" tool
+
+`` SdlAndroid::keytool ``
+   Imported executable for the keytool, a key and certificate management utility
+
+`` SdlAndroid::zip ``
+   Imported executable for the zip, for packaging and compressing files
+
+Result variables
+^^^^^^^^^^^^^^^^
+
+This module will set the following variables in your project:
+
+`` AAPT2_BIN ``
+   Path of aapt2
+
+`` APKSIGNER_BIN ``
+   Path of apksigner
+
+`` D8_BIN ``
+   Path of d8
+
+`` ZIPALIGN_BIN ``
+   Path of zipalign
+
+`` ADB_BIN ``
+   Path of adb
+
+`` KEYTOOL_BIN ``
+   Path of keytool
+
+`` ZIP_BIN ``
+   Path of zip
+
+#]=======================================================================]
+
+cmake_minimum_required(VERSION 3.7)
+
+if(NOT PROJECT_NAME MATCHES "^SDL.*")
+  message(WARNING "This module is internal to SDL and is currently not supported.")
+endif()
+
+find_package(SdlAndroidBuildTools MODULE)
+
+function(_sdl_android_find_create_imported_executable NAME)
+  string(TOUPPER "${NAME}" NAME_UPPER)
+  set(varname "${NAME_UPPER}_BIN")
+  find_program("${varname}" NAMES "${NAME}" PATHS ${SDL_ANDROID_BUILD_TOOLS_ROOT})
+  if(EXISTS "${${varname}}" AND NOT TARGET SdlAndroid::${NAME})
+    add_executable(SdlAndroid::${NAME} IMPORTED)
+    set_property(TARGET SdlAndroid::${NAME} PROPERTY IMPORTED_LOCATION "${${varname}}")
+  endif()
+endfunction()
+
+if(SdlAndroidBuildTools_FOUND)
+  _sdl_android_find_create_imported_executable(aapt2)
+  _sdl_android_find_create_imported_executable(apksigner)
+  _sdl_android_find_create_imported_executable(d8)
+  _sdl_android_find_create_imported_executable(zipalign)
+endif()
+
+_sdl_android_find_create_imported_executable(adb)
+_sdl_android_find_create_imported_executable(keytool)
+_sdl_android_find_create_imported_executable(zip)
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(SdlAndroid
+  VERSION_VAR
+  REQUIRED_VARS
+    AAPT2_BIN
+    APKSIGNER_BIN
+    D8_BIN
+    ZIPALIGN_BIN
+    KEYTOOL_BIN
+    ZIP_BIN
+)
diff --git a/cmake/android/FindSdlAndroidBuildTools.cmake b/cmake/android/FindSdlAndroidBuildTools.cmake
new file mode 100644
index 000000000000..b8825d92b572
--- /dev/null
+++ b/cmake/android/FindSdlAndroidBuildTools.cmake
@@ -0,0 +1,115 @@
+#[=======================================================================[
+
+FindSdlAndroidBuildTools
+----------------------
+
+Locate the Android build tools directory.
+
+
+Imported targets
+^^^^^^^^^^^^^^^^
+
+This find module defines the following :prop_tgt:`IMPORTED` target(s):
+
+<none>
+
+Result variables
+^^^^^^^^^^^^^^^^
+
+This module will set the following variables in your project:
+
+`` SdlAndroidBuildTools_FOUND
+   if false, no Android build tools have been found
+
+`` SDL_ANDROID_BUILD_TOOLS_ROOT
+   path of the Android build tools root directory if found
+
+`` SDL_ANDROID_BUILD_TOOLS_VERSION
+   the human-readable string containing the android build tools version if found
+
+Cache variables
+^^^^^^^^^^^^^^^
+
+These variables may optionally be set to help this module find the correct files:
+
+``SDL_ANDROID_BUILD_TOOLS_ROOT``
+  path of the Android build tools root directory
+
+
+Variables for locating Android platform
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This module responds to the flags:
+
+``SDL_ANDROID_HOME
+    First, this module will look for platforms in this CMake variable.
+
+``ANDROID_HOME
+    If no platform was found in `SDL_ANDROID_HOME`, then try `ANDROID_HOME`.
+
+``$ENV{ANDROID_HOME}
+    If no platform was found in neither `SDL_ANDROID_HOME` or `ANDROID_HOME`, then try `ANDROID_HOME}`
+
+#]=======================================================================]
+
+cmake_minimum_required(VERSION 3.7)
+
+if(NOT PROJECT_NAME MATCHES "^SDL.*")
+  message(WARNING "This module is internal to SDL and is currently not supported.")
+endif()
+
+function(_sdl_is_valid_android_build_tools_root RESULT VERSION BUILD_TOOLS_ROOT)
+  set(result TRUE)
+  set(version -1)
+
+  string(REGEX MATCH "/([0-9.]+)$" root_match "${BUILD_TOOLS_ROOT}")
+  if(root_match
+      AND EXISTS "${BUILD_TOOLS_ROOT}/aapt2"
+      AND EXISTS "${BUILD_TOOLS_ROOT}/apksigner"
+      AND EXISTS "${BUILD_TOOLS_ROOT}/d8"
+      AND EXISTS "${BUILD_TOOLS_ROOT}/zipalign")
+    set(result "${BUILD_TOOLS_ROOT}")
+    set(version "${CMAKE_MATCH_1}")
+  endif()
+
+  set(${RESULT} ${result} PARENT_SCOPE)
+  set(${VERSION} ${version} PARENT_SCOPE)
+endfunction()
+
+function(_find_sdl_android_build_tools_root ROOT)
+  cmake_parse_arguments(fsabtr "" "" "" ${ARGN})
+  set(homes ${SDL_ANDROID_HOME} ${ANDROID_HOME} $ENV{ANDROID_HOME})
+  set(root ${ROOT}-NOTFOUND)
+  foreach(home IN LISTS homes)
+    if(NOT IS_DIRECTORY "${home}")
+      continue()
+    endif()
+    file(GLOB build_tools_roots LIST_DIRECTORIES true "${home}/build-tools/*")
+    set(max_build_tools_version -1)
+    set(max_build_tools_root "")
+    foreach(build_tools_root IN LISTS build_tools_roots)
+      _sdl_is_valid_android_build_tools_root(is_valid build_tools_version "${build_tools_root}")
+      if(is_valid AND build_tools_version GREATER max_build_tools_version)
+        set(max_build_tools_version "${build_tools_version}")
+        set(max_build_tools_root "${build_tools_root}")
+      endif()
+    endforeach()
+    if(max_build_tools_version GREATER -1)
+      set(root ${max_build_tools_root})
+      break()
+    endif()
+  endforeach()
+  set(${ROOT} ${root} PARENT_SCOPE)
+endfunction()
+
+if(NOT DEFINED SDL_ANDROID_BUILD_TOOLS_ROOT)
+  _find_sdl_android_build_tools_root(SDL_ANDROID_BUILD_TOOLS_ROOT)
+  set(SDL_ANDROID_BUILD_TOOLS_ROOT "${SDL_ANDROID_BUILD_TOOLS_ROOT}" CACHE PATH "Path of Android build tools")
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(SdlAndroidBuildTools
+  VERSION_VAR SDL_ANDROID_BUILD_TOOLS_VERSION
+  REQUIRED_VARS SDL_ANDROID_BUILD_TOOLS_ROOT
+)
diff --git a/cmake/android/FindSdlAndroidPlatform.cmake b/cmake/android/FindSdlAndroidPlatform.cmake
new file mode 100644
index 000000000000..0f9584906caf
--- /dev/null
+++ b/cmake/android/FindSdlAndroidPlatform.cmake
@@ -0,0 +1,123 @@
+#[=======================================================================[
+
+FindSdlAndroidPlatform
+----------------------
+
+Locate the Android SDK platform.
+
+
+Imported targets
+^^^^^^^^^^^^^^^^
+
+This module defines the following :prop_tgt:`IMPORTED` target(s):
+
+<none>
+
+Result variables
+^^^^^^^^^^^^^^^^
+
+This find module will set the following variables in your project:
+
+`` SdlAndroidPlatform_FOUND
+   if false, no Android platform has been found
+
+`` SDL_ANDROID_PLATFORM_ROOT
+   path of the Android SDK platform root directory if found
+
+`` SDL_ANDROID_PLATFORM_ANDROID_JAR
+   path of the Android SDK platform jar file if found
+
+`` SDL_ANDROID_PLATFORM_VERSION
+   the human-readable string containing the android platform version if found
+
+Cache variables
+^^^^^^^^^^^^^^^
+
+These variables may optionally be set to help this module find the correct files:
+
+``SDL_ANDROID_PLATFORM_ROOT``
+  path of the Android SDK platform root directory
+
+
+Variables for locating Android platform
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This module responds to the flags:
+
+``SDL_ANDROID_HOME
+    First, this module will look for platforms in this CMake variable.
+
+``ANDROID_HOME
+    If no platform was found in `SDL_ANDROID_HOME`, then try `ANDROID_HOME`.
+
+``$ENV{ANDROID_HOME}
+    If no platform was found in neither `SDL_ANDROID_HOME` or `ANDROID_HOME`, then try `ANDROID_HOME}`
+
+#]=======================================================================]
+
+cmake_minimum_required(VERSION 3.7)
+
+if(NOT PROJECT_NAME MATCHES "^SDL.*")
+    message(WARNING "This module is internal to SDL and is currently not supported.")
+endif()
+
+function(_sdl_is_valid_android_platform_root RESULT VERSION PLATFORM_ROOT)
+    set(result FALSE)
+    set(version -1)
+
+    string(REGEX MATCH "/android-([0-9]+)$" root_match "${PLATFORM_ROOT}")
+    if(root_match AND EXISTS "${PLATFORM_ROOT}/android.jar")
+        set(result TRUE)
+        set(version "${CMAKE_MATCH_1}")
+    endif()
+
+    set(${RESULT} ${result} PARENT_SCOPE)
+    set(${VERSION} ${version} PARENT_SCOPE)
+endfunction()
+
+function(_sdl_find_android_platform_root ROOT)
+  cmake_parse_arguments(sfapr "" "" "" ${ARGN})
+  set(homes ${SDL_ANDROID_HOME} ${ANDROID_HOME} $ENV{ANDROID_HOME})
+  set(root ${ROOT}-NOTFOUND)
+  foreach(home IN LISTS homes)
+    if(NOT IS_DIRECTORY "${home}")
+      continue()
+    endif()
+    file(GLOB platform_roots LIST_DIRECTORIES true "${home}/platforms/*")
+    set(max_platform_version -1)
+    set(max_platform_root "")
+    foreach(platform_root IN LISTS platform_roots)
+      _sdl_is_valid_android_platform_root(is_valid platform_version "${platform_root}")
+      if(is_valid AND platform_version GREATER max_platform_version)
+        set(max_platform_version "${platform_version}")
+        set(max_platform_root "${platform_root}")
+      endif()
+    endforeach()
+    if(max_platform_version GREATER -1)
+      set(root ${max_platform_root})
+      break()
+    endif()
+  endforeach()
+  set(${ROOT} ${root} PARENT_SCOPE)
+endfunction()
+
+set(SDL_ANDROID_PLATFORM_ANDROID_JAR "SDL_ANDROID_PLATFORM_ANDROID_JAR-NOTFOUND")
+
+if(NOT DEFINED SDL_ANDROID_PLATFORM_ROOT)
+  _sdl_find_android_platform_root(SDL_ANDROID_PLATFORM_ROOT)
+  set(SDL_ANDROID_PLATFORM_ROOT "${SDL_ANDROID_PLATFORM_ROOT}" CACHE PATH "Path of Android platform")
+endif()
+if(SDL_ANDROID_PLATFORM_ROOT)
+  _sdl_is_valid_android_platform_root(_valid SDL_ANDROID_PLATFORM_VERSION "${SDL_ANDROID_PLATFORM_ROOT}")
+  if(_valid)
+    set(SDL_ANDROID_PLATFORM_ANDROID_JAR "${SDL_ANDROID_PLATFORM_ROOT}/android.jar")
+  endif()
+  unset(_valid)
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(SdlAndroidPlatform
+  VERSION_VAR SDL_ANDROID_PLATFORM_VERSION
+  REQUIRED_VARS SDL_ANDROID_PLATFORM_ROOT SDL_ANDROID_PLATFORM_ANDROID_JAR
+)
diff --git a/cmake/android/SdlAndroidFunctions.cmake b/cmake/android/SdlAndroidFunctions.cmake
new file mode 100644
index 000000000000..b7b3aa84962f
--- /dev/null
+++ b/cmake/android/SdlAndroidFunctions.cmake
@@ -0,0 +1,276 @@
+#[=======================================================================[
+
+This CMake script contains functions to build an Android APK.
+It is (currently) limited to packaging binaries for a single architecture.
+
+#]=======================================================================]
+
+cmake_minimum_required(VERSION 3.7)
+
+if(NOT PROJECT_NAME MATCHES "^SDL.*")
+  message(WARNING "This module is internal to SDL and is currently not supported.")
+endif()
+
+function(_sdl_create_outdir_for_target OUTDIRECTORY TARGET)
+  set(outdir "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TARGET}.dir")
+  # Some CMake versions have a slow `cmake -E make_directory` implementation
+  if(NOT IS_DIRECTORY "${outdir}")
+    execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${outdir}")
+  endif()
+  set("${OUTDIRECTORY}" "${outdir}" PARENT_SCOPE)
+endfunction()
+
+function(sdl_create_android_debug_keystore TARGET)
+  set(output "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_debug.keystore")
+  add_custom_command(OUTPUT ${output}
+    COMMAND ${CMAKE_COMMAND} -E rm -f "${output}"
+    COMMAND SdlAndroid::keytool -genkey -keystore "${output}" -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "C=US, O=Android, CN=Android Debug"
+  )
+  add_custom_target(${TARGET} DEPENDS "${output}")
+  set_property(TARGET ${TARGET} PROPERTY OUTPUT "${output}")
+endfunction()
+
+function(sdl_android_compile_resources TARGET)
+  cmake_parse_arguments(arg "" "RESFOLDER" "RESOURCES" ${ARGN})
+
+  if(NOT arg_RESFOLDER AND NOT arg_RESOURCES)
+    message(FATAL_ERROR "Missing RESFOLDER or RESOURCES argument (need one or both)")
+  endif()
+  _sdl_create_outdir_for_target(outdir "${TARGET}")
+  set(out_files "")
+
+  set(res_files "")
+  if(arg_RESFOLDER)
+    get_filename_component(arg_RESFOLDER "${arg_RESFOLDER}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+    file(GLOB_RECURSE res_folder_files "${arg_RESFOLDER}/*")
+    list(APPEND res_files ${res_folder_files})
+
+    foreach(res_file IN LISTS res_files)
+      file(RELATIVE_PATH rel_res_file "${arg_RESFOLDER}" "${res_file}")
+      string(REPLACE "/" "_" rel_comp_path "${rel_res_file}")
+      if(res_file MATCHES ".*res/values.*\\.xml$")
+        string(REGEX REPLACE "\\.xml" ".arsc" rel_comp_path "${rel_comp_path}")
+      endif()
+      set(comp_path "${outdir}/${rel_comp_path}.flat")
+      add_custom_command(
+        OUTPUT "${comp_path}"
+        COMMAND SdlAndroid::aapt2 compile -o "${outdir}" "${res_file}"
+        DEPENDS ${res_file}
+      )
+      list(APPEND out_files "${comp_path}")
+    endforeach()
+  endif()
+
+  if(arg_RESOURCES)
+    list(APPEND res_files ${arg_RESOURCES})
+    foreach(res_file IN LISTS arg_RESOURCES)
+      string(REGEX REPLACE ".*/res/" "" rel_res_file ${res_file})
+      string(REPLACE "/" "_" rel_comp_path "${rel_res_file}")
+      if(res_file MATCHES ".*res/values.*\\.xml$")
+        string(REGEX REPLACE "\\.xml" ".arsc" rel_comp_path "${rel_comp_path}")
+      endif()
+      set(comp_path "${outdir}/${rel_comp_path}.flat")
+      add_custom_command(
+        OUTPUT "${comp_path}"
+        COMMAND SdlAndroid::aapt2 compile -o "${outdir}" "${res_file}"
+        DEPENDS ${res_file}
+      )
+      list(APPEND out_files "${comp_path}")
+    endforeach()
+  endif()
+
+  add_custom_target(${TARGET} DEPENDS ${out_files})
+  set_property(TARGET "${TARGET}" PROPERTY OUTPUTS "${out_files}")
+  set_property(TARGET "${TARGET}" PROPERTY SOURCES "${res_files}")
+endfunction()
+
+function(sdl_android_link_resources TARGET)
+  cmake_parse_arguments(arg "NO_DEBUG" "MIN_SDK_VERSION;TARGET_SDK_VERSION;ANDROID_JAR;OUTPUT_APK;MANIFEST;PACKAGE" "RES_TARGETS" ${ARGN})
+
+  if(arg_MANIFEST)
+    get_filename_component(arg_MANIFEST "${arg_MANIFEST}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+  else()
+    message(FATAL_ERROR "sdl_add_android_link_resources_target requires a Android MANIFEST path (${arg_MANIFEST})")
+  endif()
+  if(NOT arg_PACKAGE)
+    file(READ "${arg_MANIFEST}" manifest_contents)
+    string(REGEX MATCH "package=\"([a-zA-Z0-9_.]+)\"" package_match "${manifest_contents}")
+    if(NOT package_match)
+      message(FATAL_ERROR "Could not extract package from Android manifest (${arg_MANIFEST})")
+    endif()
+    set(arg_PACKAGE "${CMAKE_MATCH_1}")
+  endif()
+
+  set(depends "")
+
+  _sdl_create_outdir_for_target(outdir "${TARGET}")
+  string(REPLACE "." "/" java_r_path "${arg_PACKAGE}")
+  get_filename_component(java_r_path "${java_r_path}" ABSOLUTE BASE_DIR "${outdir}")
+  set(java_r_path "${java_r_path}/R.java")
+
+  set(command SdlAndroid::aapt2 link)
+  if(NOT arg_NO_DEBUG)
+    list(APPEND command --debug-mode)
+  endif()
+  if(arg_MIN_SDK_VERSION)
+    list(APPEND command --min-sdk-version ${arg_MIN_SDK_VERSION})
+  endif()
+  if(arg_TARGET_SDK_VERSION)
+    list(APPEND command --target-sdk-version ${arg_TARGET_SDK_VERSION})
+  endif()
+  if(arg_ANDROID_JAR)
+    list(APPEND command -I "${arg_ANDROID_JAR}")
+  else()
+    list(APPEND command -I "${SDL_ANDROID_PLATFORM_ANDROID_JAR}")
+  endif()
+  if(NOT arg_OUTPUT_APK)
+    set(arg_OUTPUT_APK "${TARGET}.apk")
+  endif()
+  get_filename_component(arg_OUTPUT_APK "${arg_OUTPUT_APK}" ABSOLUTE BASE_DIR "${outdir}")
+  list(APPEND command -o "${arg_OUTPUT_APK}")
+  list(APPEND command --java "${outdir}")
+  list(APPEND command --manifest "${arg_MANIFEST}")
+  foreach(res_target IN LISTS arg_RES_TARGETS)
+    list(APPEND command $<TARGET_PROPERTY:${res_target},OUTPUTS>)
+    list(APPEND depends $<TARGET_PROPERTY:${res_target},OUTPUTS>)
+  endforeach()
+  add_custom_command(
+    OUTPUT "${arg_OUTPUT_APK}" "${java_r_path}"
+    COMMAND ${command}
+    DEPENDS ${depends} ${arg_MANIFEST}
+    COMMAND_EXPAND_LISTS
+    VERBATIM
+  )
+  add_custom_target(${TARGET} DEPENDS "${arg_OUTPUT_APK}" "${java_r_path}")
+  set_property(TARGET ${TARGET} PROPERTY OUTPUT "${arg_OUTPUT_APK}")
+  set_property(TARGET ${TARGET} PROPERTY JAVA_R "${java_r_path}")
+  set_property(TARGET ${TARGET} PROPERTY OUTPUTS "${${arg_OUTPUT_APK}};${java_r_path}")
+endfunction()
+
+function(sdl_add_to_apk_unaligned TARGET)
+  cmake_parse_arguments(arg "" "APK_IN;NAME;OUTDIR" "ASSETS;NATIVE_LIBS;DEX" ${ARGN})
+
+  if(NOT arg_APK_IN)
+    message(FATAL_ERROR "Missing APK_IN argument")
+  endif()
+
+  if(NOT TARGET ${arg_APK_IN})
+    message(FATAL_ERROR "APK_IN (${arg_APK_IN}) must be a target providing an apk")
+  endif()
+
+  _sdl_create_outdir_for_target(workdir ${TARGET})
+
+  if(NOT arg_OUTDIR)
+    set(arg_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}")
+  endif()
+
+  if(NOT arg_NAME)
+    string(REGEX REPLACE "[:-]+" "." arg_NAME "${TARGET}")
+    if(NOT arg_NAME MATCHES "\\.apk")
+      set(arg_NAME "${arg_NAME}.apk")
+    endif()
+  endif()
+  get_filename_component(apk_file "${arg_NAME}" ABSOLUTE BASE_DIR "${arg_OUTDIR}")
+
+  set(apk_libdir "lib/${ANDROID_ABI}")
+
+  set(depends "")
+
+  set(commands
+    COMMAND "${CMAKE_COMMAND}" -E remove_directory -rf "${apk_libdir}" "assets"
+    COMMAND "${CMAKE_COMMAND}" -E make_directory "${apk_libdir}" "assets"
+    COMMAND "${CMAKE_COMMAND}" -E copy "$<TARGET_PROPERTY:${arg_APK_IN},OUTPUT>" "${apk_file}"
+  )
+
+  set(dex_i "1")
+  foreach(dex IN LISTS arg_DEX)
+    set(suffix "${dex_i}")
+    if(suffix STREQUAL "1")
+      set(suffix "")
+    endif()
+    list(APPEND commands
+      COMMAND "${CMAKE_COMMAND}" -E copy "$<TARGET_PROPERTY:${dex},OUTPUT>" "classes${suffix}.dex"
+      COMMAND SdlAndroid::zip -u -q -j "${apk_file}" "classes${suffix}.dex"
+    )
+    math(EXPR dex_i "${dex_i}+1")
+    list(APPEND depends "$<TARGET_PROPERTY:${dex},OUTPUT>")
+  endforeach()
+
+  foreach(native_lib IN LISTS arg_NATIVE_LIBS)
+    list(APPEND commands
+      COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:${native_lib}> "${apk_libdir}/$<TARGET_FILE_NAME:${native_lib}>"
+      COMMAND SdlAndroid::zip -u -q "${apk_file}" "${apk_libdir}/$<TARGET_FILE_NAME:${native_lib}>"
+    )
+  endforeach()
+  if(arg_ASSETS)
+    list(APPEND commands
+      COMMAND "${CMAKE_COMMAND}" -E copy ${arg_ASSETS} "assets"
+      COMMAND SdlAndroid::zip -u -r -q "${apk_file}" "assets"
+    )
+  endif()
+
+  add_custom_command(OUTPUT "${apk_file}"
+    ${commands}
+    DEPENDS ${arg_NATIVE_LIBS} ${depends} "$<TARGET_PROPERTY:${arg_APK_IN},OUTPUT>"
+    WORKING_DIRECTORY "${workdir}"
+  )
+  add_custom_target(${TARGET} DEPENDS "${apk_file}")
+  set_property(TARGET ${TARGET} PROPERTY OUTPUT "${apk_file}")
+endfunction()
+
+function(sdl_apk_align TARGET APK_IN)
+  cmake_parse_arguments(arg "" "NAME;OUTDIR" "" ${ARGN})
+
+  if(NOT TARGET ${arg_APK_IN})
+    message(FATAL_ERROR "APK_IN (${arg_APK_IN}) must be a target providing an apk")
+  endif()
+
+  if(NOT arg_OUTDIR)
+    set(arg_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}")
+  endif()
+
+  if(NOT arg_NAME)
+    string(REGEX REPLACE "[:-]+" "." arg_NAME "${TARGET}")
+    if(NOT arg_NAME MATCHES "\\.apk")
+      set(arg_NAME "${arg_NAME}.apk")
+    endif()
+  endif()
+  get_filename_component(apk_file "${arg_NAME}" ABSOLUTE BASE_DIR "${arg_OUTDIR}")
+
+  add_custom_command(OUTPUT "${apk_file}"
+    COMMAND SdlAndroid::zipalign -f 4 "$<TARGET_PROPERTY:${APK_IN},OUTPUT>" "${apk_file}"
+    DEPENDS "$<TARGET_PROPERTY:${APK_IN},OUTPUT>"
+  )
+  add_custom_target(${TARGET} DEPENDS "${apk_file}")
+  set_property(TARGET ${TARGET} PROPERTY OUTPUT "${apk_file}")
+endfunction()
+
+function(sdl_apk_sign TARGET APK_IN)
+  cmake_parse_arguments(arg "" "OUTPUT;KEYSTORE" "" ${ARGN})
+
+  if

(Patch may be truncated, please check the link at the top of this post.)