From 5ae2104c9a91eea9c7cd7777ee1d58b4d5d2532a Mon Sep 17 00:00:00 2001
From: Angga Permana <86430023+anggape@users.noreply.github.com>
Date: Wed, 23 Aug 2023 19:18:50 +0700
Subject: [PATCH] Add experimental Android support (#190)

* Add `Raylib-cs.Android` project
* Update ci to handle android native libraries
* Update ci to pack android project
---
 .github/workflows/build.yml                   | 80 +++++++++++++++++--
 Directory.Build.props                         |  9 +++
 Raylib-cs.Android/Raylib-cs.Android.csproj    | 15 ++++
 Raylib-cs.Android/RaylibActivity.cs           | 17 ++++
 Raylib-cs.Android/android.patch               | 27 +++++++
 .../runtimes/android-arm/native/.keep         |  0
 .../runtimes/android-arm64/native/.keep       |  0
 .../runtimes/android-x64/native/.keep         |  0
 .../runtimes/android-x86/native/.keep         |  0
 Raylib-cs.sln                                 | 14 ++++
 Raylib-cs/Raylib-cs.csproj                    |  3 -
 11 files changed, 156 insertions(+), 9 deletions(-)
 create mode 100644 Directory.Build.props
 create mode 100644 Raylib-cs.Android/Raylib-cs.Android.csproj
 create mode 100644 Raylib-cs.Android/RaylibActivity.cs
 create mode 100644 Raylib-cs.Android/android.patch
 create mode 100644 Raylib-cs.Android/runtimes/android-arm/native/.keep
 create mode 100644 Raylib-cs.Android/runtimes/android-arm64/native/.keep
 create mode 100644 Raylib-cs.Android/runtimes/android-x64/native/.keep
 create mode 100644 Raylib-cs.Android/runtimes/android-x86/native/.keep

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a22e2b4..0f50a56 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -22,8 +22,52 @@ jobs:
         id: version
         shell: bash
         run: |
-          echo "version=$(sed -n 's/.*<TargetRaylibTag>\(.*\)<\/TargetRaylibTag>.*/\1/p' Raylib-cs/Raylib-cs.csproj)">> ${GITHUB_OUTPUT}
-          echo "pkgversion=$(sed -n 's/.*<PackageVersion>\(.*\)<\/PackageVersion>.*/\1/p' Raylib-cs/Raylib-cs.csproj)">> ${GITHUB_OUTPUT}
+          echo "version=$(sed -n 's/.*<TargetRaylibTag>\(.*\)<\/TargetRaylibTag>.*/\1/p' Directory.Build.props)">> ${GITHUB_OUTPUT}
+          echo "pkgversion=$(sed -n 's/.*<PackageVersion>\(.*\)<\/PackageVersion>.*/\1/p' Directory.Build.props)">> ${GITHUB_OUTPUT}
+
+  build-android:
+    runs-on: ubuntu-latest
+    needs: prepare
+    strategy:
+      matrix:
+        name: [arm, arm64, x86, x64]
+        include:
+          - name: arm
+            arch: armeabi-v7a
+          - name: arm64
+            arch: arm64-v8a
+          - name: x86
+            arch: x86
+          - name: x64
+            arch: x86_64
+    steps:
+      - name: checkout repository
+        uses: actions/checkout@v3
+
+      - name: build raylib
+        env:
+          version: ${{ needs.prepare.outputs.version }}
+        run: |
+          curl -Lso raylib.zip https://github.com/raysan5/raylib/archive/refs/tags/${version}.zip
+          unzip -qq raylib.zip
+          pushd raylib-${version}; patch -p1 < ../Raylib-cs.Android/android.patch; popd
+          cmake -S raylib-${version} \
+            -B build \
+            -D CMAKE_BUILD_TYPE=Release \
+            -D BUILD_SHARED_LIBS=ON \
+            -D BUILD_EXAMPLES=OFF \
+            -D CMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \
+            -D PLATFORM=Android \
+            -D ANDROID_ABI=${{ matrix.arch }} \
+            -D ANDROID_PLATFORM=21
+          cmake --build build --config Release
+
+      - name: upload build
+        uses: actions/upload-artifact@v3
+        with:
+          name: android-${{ matrix.name }}
+          path: build/raylib/libraylib.so
+          if-no-files-found: error
 
   build-linux:
     runs-on: ubuntu-latest
@@ -133,6 +177,7 @@ jobs:
     runs-on: ubuntu-latest
     needs:
       - prepare
+      - build-android
       - build-linux
       - build-osx
       - build-windows
@@ -140,6 +185,26 @@ jobs:
       - name: checkout repository
         uses: actions/checkout@v3
 
+      - uses: actions/download-artifact@v3
+        with:
+          name: android-arm
+          path: Raylib-cs.Android/runtimes/android-arm/native
+
+      - uses: actions/download-artifact@v3
+        with:
+          name: android-arm64
+          path: Raylib-cs.Android/runtimes/android-arm64/native
+
+      - uses: actions/download-artifact@v3
+        with:
+          name: android-x86
+          path: Raylib-cs.Android/runtimes/android-x86/native
+
+      - uses: actions/download-artifact@v3
+        with:
+          name: android-x64
+          path: Raylib-cs.Android/runtimes/android-x64/native
+
       - uses: actions/download-artifact@v3
         with:
           name: linux-x64
@@ -170,23 +235,26 @@ jobs:
         with:
           dotnet-version: 6.0.x
 
+      - name: Restore workload
+        run: dotnet workload restore
+
       - name: Create NuGet Package
-        run: dotnet pack -c Release Raylib-cs
+        run: dotnet pack -c Release --output nuget
 
       - name: Upload NuGet Package As Artifact
         uses: actions/upload-artifact@v3
         with:
-          path: Raylib-cs/bin/Release/Raylib-cs.${{ needs.prepare.outputs.pkgversion }}.*pkg
+          path: nuget/*
 
       - name: Upload NuGet Package As Release
         uses: softprops/action-gh-release@v1
         if: startsWith(github.ref, 'refs/tags/')
         with:
-          files: Raylib-cs/bin/Release/Raylib-cs.${{ needs.prepare.outputs.pkgversion }}.*pkg
+          files: nuget/*
 
       - name: Publish to NuGet
         if: startsWith(github.ref, 'refs/tags/')
         run: |
           dotnet nuget push \
-            Raylib-cs/bin/Release/Raylib-cs.${{ needs.prepare.outputs.pkgversion }}.nupkg \
+            nuget/* \
             --api-key ${{secrets.NUGET_API_KEY}}
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..2ff4fec
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,9 @@
+<Project>
+
+  <PropertyGroup>
+    <TargetRaylibTag>4.5.0</TargetRaylibTag>
+    <Version>4.5.0.4</Version>
+    <PackageVersion>4.5.0.4</PackageVersion>
+  </PropertyGroup>
+
+</Project>
\ No newline at end of file
diff --git a/Raylib-cs.Android/Raylib-cs.Android.csproj b/Raylib-cs.Android/Raylib-cs.Android.csproj
new file mode 100644
index 0000000..09525f5
--- /dev/null
+++ b/Raylib-cs.Android/Raylib-cs.Android.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>net6.0-android;net7.0-android</TargetFrameworks>
+    <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
+    <RootNamespace>Raylib_cs</RootNamespace>
+    <Nullable>enable</Nullable>
+    <ImplicitUsings>enable</ImplicitUsings>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="../Raylib-cs/Raylib-cs.csproj" />
+  </ItemGroup>
+
+</Project>
\ No newline at end of file
diff --git a/Raylib-cs.Android/RaylibActivity.cs b/Raylib-cs.Android/RaylibActivity.cs
new file mode 100644
index 0000000..14d7c54
--- /dev/null
+++ b/Raylib-cs.Android/RaylibActivity.cs
@@ -0,0 +1,17 @@
+using System.Runtime.InteropServices;
+
+namespace Raylib_cs;
+
+public abstract class RaylibActivity : NativeActivity
+{
+    protected override void OnCreate(Bundle? savedInstanceState)
+    {
+        base.OnCreate(savedInstanceState);
+        RaylibSetAndroidCallback(OnReady);
+    }
+
+    protected abstract void OnReady();
+
+    [DllImport(Raylib.NativeLibName, CallingConvention = CallingConvention.Cdecl)]
+    private static extern void RaylibSetAndroidCallback(Action callback);
+}
diff --git a/Raylib-cs.Android/android.patch b/Raylib-cs.Android/android.patch
new file mode 100644
index 0000000..6d70c48
--- /dev/null
+++ b/Raylib-cs.Android/android.patch
@@ -0,0 +1,27 @@
+diff --git a/src/rcore.c b/src/rcore.c
+index eae4951..4400b22 100644
+--- a/src/rcore.c
++++ b/src/rcore.c
+@@ -1,3 +1,22 @@
++#if defined (PLATFORM_ANDROID)
++#include <stdlib.h>
++
++typedef void (*RaylibAndroidCallback)();
++static RaylibAndroidCallback _androidCallback = NULL;
++
++void RaylibSetAndroidCallback(RaylibAndroidCallback callback) {
++  _androidCallback = callback;
++}
++
++int main(int argc, char *argv[]) {
++  (void)argc;
++  (void)argv;
++  while (_androidCallback == NULL) {
++  }
++  _androidCallback();
++}
++#endif
++
+ /**********************************************************************************************
+ *
+ *   rcore - Basic functions to manage windows, OpenGL context and input on multiple platforms
diff --git a/Raylib-cs.Android/runtimes/android-arm/native/.keep b/Raylib-cs.Android/runtimes/android-arm/native/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/Raylib-cs.Android/runtimes/android-arm64/native/.keep b/Raylib-cs.Android/runtimes/android-arm64/native/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/Raylib-cs.Android/runtimes/android-x64/native/.keep b/Raylib-cs.Android/runtimes/android-x64/native/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/Raylib-cs.Android/runtimes/android-x86/native/.keep b/Raylib-cs.Android/runtimes/android-x86/native/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/Raylib-cs.sln b/Raylib-cs.sln
index 823188f..6e28f1f 100644
--- a/Raylib-cs.sln
+++ b/Raylib-cs.sln
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Raylib-cs", "Raylib-cs\Rayl
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Raylib-cs.Tests", "Raylib-cs.Tests\Raylib-cs.Tests.csproj", "{523DEE6A-20CD-47AB-94A6-8D3C3CF9ADAD}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Raylib-cs.Android", "Raylib-cs.Android\Raylib-cs.Android.csproj", "{8C2B9C24-3926-4B3A-902C-6429CF4864AA}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -25,6 +27,18 @@ Global
 		{523DEE6A-20CD-47AB-94A6-8D3C3CF9ADAD}.Release|Any CPU.Build.0 = Release|Any CPU
 		{523DEE6A-20CD-47AB-94A6-8D3C3CF9ADAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{523DEE6A-20CD-47AB-94A6-8D3C3CF9ADAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Debug|x64.Build.0 = Debug|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Debug|x86.Build.0 = Debug|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Release|x64.ActiveCfg = Release|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Release|x64.Build.0 = Release|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Release|x86.ActiveCfg = Release|Any CPU
+		{8C2B9C24-3926-4B3A-902C-6429CF4864AA}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/Raylib-cs/Raylib-cs.csproj b/Raylib-cs/Raylib-cs.csproj
index 78518fd..47d8600 100644
--- a/Raylib-cs/Raylib-cs.csproj
+++ b/Raylib-cs/Raylib-cs.csproj
@@ -11,9 +11,6 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <TargetRaylibTag>4.5.0</TargetRaylibTag>
-    <Version>4.5.0.4</Version>
-    <PackageVersion>4.5.0.4</PackageVersion>
     <Authors>Chris Dill, Raysan5</Authors>
     <PackProject>true</PackProject>
     <IncludeSymbols>true</IncludeSymbols>