diff --git a/.github/workflows/plugin-apk.yml b/.github/workflows/plugin-apk.yml
new file mode 100644
index 00000000..0b119349
--- /dev/null
+++ b/.github/workflows/plugin-apk.yml
@@ -0,0 +1,56 @@
+name: MobileGL Plugin APK
+
+on:
+ push:
+ branches:
+ - dev
+ - Feat/Backend-Direct-GLES
+ - Feat/Backend-Direct-Vulkan
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+
+ - name: Set artifact metadata
+ run: |
+ echo "date_today=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV"
+ echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_ENV"
+
+ - name: Set up JDK
+ uses: actions/setup-java@v4
+ with:
+ distribution: zulu
+ java-version: '17'
+
+ - name: Setup Gradle
+ uses: gradle/actions/setup-gradle@v4
+ with:
+ gradle-version: 8.10.2
+
+ - name: Setup Android SDK
+ uses: android-actions/setup-android@v3
+
+ - name: Install Android NDK
+ run: |
+ sdkmanager "ndk;27.3.13750724"
+ echo "ndk.dir=$ANDROID_HOME/ndk/27.3.13750724" >> android-plugin/local.properties
+
+ - name: Update glslang external sources
+ working-directory: 3rdparty/glslang
+ run: python update_glslang_sources.py
+
+ - name: Build plugin APK
+ run: gradle --no-daemon -p android-plugin :app:assembleRelease
+
+ - name: Upload plugin APK
+ uses: actions/upload-artifact@v4
+ with:
+ name: MobileGL-plugin-${{ env.date_today }}-${{ env.short_sha }}
+ path: android-plugin/app/build/outputs/apk/release/*.apk
diff --git a/.gitignore b/.gitignore
index 53c88dc7..d240ff91 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,4 +18,7 @@ MobileGL/MG_Test/build
/cmake-build*
.idea
MobileGL/MG*/build*
-MobileGL/MG*/cmake-build*
\ No newline at end of file
+MobileGL/MG*/cmake-build*
+/android-plugin/.gradle
+/android-plugin/build
+/android-plugin/app/build
diff --git a/android-plugin/app/build.gradle b/android-plugin/app/build.gradle
new file mode 100644
index 00000000..477c1cbd
--- /dev/null
+++ b/android-plugin/app/build.gradle
@@ -0,0 +1,53 @@
+plugins {
+ id 'com.android.application'
+}
+
+def mobileGlEnv = [
+ LIBGL_ES : '3',
+ POJAV_RENDERER : 'opengles3',
+ MOBILEGL_BACKEND_TYPE: 'DirectGLES',
+]
+
+def mobileGlEnvString = mobileGlEnv.collect { key, value -> "${key}=${value}" }.join(':')
+
+android {
+ namespace 'top.mobilegl.plugin'
+ compileSdk 34
+ ndkVersion '27.3.13750724'
+
+ defaultConfig {
+ applicationId 'top.mobilegl.plugin'
+ minSdk 26
+ targetSdk 34
+ versionCode 1
+ versionName 'dev'
+
+ ndk {
+ abiFilters 'arm64-v8a'
+ }
+
+ manifestPlaceholders = [
+ des : 'MobileGL',
+ renderer: 'MobileGL:libMobileGL.so:/libMobileGL.so',
+ boatEnv : mobileGlEnvString,
+ pojavEnv: mobileGlEnvString,
+ ]
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+
+ packaging {
+ jniLibs {
+ useLegacyPackaging true
+ excludes += ['**/libSPIRV-Tools-shared.so']
+ }
+ }
+}
+
+dependencies {
+ implementation project(':MobileGL')
+}
diff --git a/android-plugin/app/src/main/AndroidManifest.xml b/android-plugin/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..bfa2586e
--- /dev/null
+++ b/android-plugin/app/src/main/AndroidManifest.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android-plugin/app/src/main/java/top/mobilegl/plugin/PluginActivity.java b/android-plugin/app/src/main/java/top/mobilegl/plugin/PluginActivity.java
new file mode 100644
index 00000000..58a73e77
--- /dev/null
+++ b/android-plugin/app/src/main/java/top/mobilegl/plugin/PluginActivity.java
@@ -0,0 +1,12 @@
+package top.mobilegl.plugin;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public final class PluginActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ finish();
+ }
+}
diff --git a/android-plugin/app/src/main/res/values/strings.xml b/android-plugin/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..d740167f
--- /dev/null
+++ b/android-plugin/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ MobileGL
+
diff --git a/android-plugin/app/src/main/res/values/styles.xml b/android-plugin/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..bb606254
--- /dev/null
+++ b/android-plugin/app/src/main/res/values/styles.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android-plugin/build.gradle b/android-plugin/build.gradle
new file mode 100644
index 00000000..f25c7bf0
--- /dev/null
+++ b/android-plugin/build.gradle
@@ -0,0 +1,11 @@
+plugins {
+ id 'com.android.application' version '8.6.0' apply false
+ id 'com.android.library' version '8.6.0' apply false
+}
+
+subprojects { subproject ->
+ subproject.plugins.withId('com.android.library') {
+ subproject.android.defaultConfig.ndk.abiFilters.clear()
+ subproject.android.defaultConfig.ndk.abiFilters.add('arm64-v8a')
+ }
+}
diff --git a/android-plugin/settings.gradle b/android-plugin/settings.gradle
new file mode 100644
index 00000000..a3b1bb7b
--- /dev/null
+++ b/android-plugin/settings.gradle
@@ -0,0 +1,20 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = 'MobileGLPlugin'
+include ':app'
+include ':MobileGL'
+project(':MobileGL').projectDir = file('..')