Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f4bc233
Add android gradle build
trulio2 Aug 2, 2026
dfaab7b
Merge branch 'master' of https://github.com/RednibCoding/OpenShadowFl…
trulio2 Aug 2, 2026
25d7eeb
add gh workflow
trulio2 Aug 2, 2026
c31aa16
Merge branch 'master' of https://github.com/RednibCoding/OpenShadowFl…
trulio2 Aug 2, 2026
c0da8a6
Merge branch 'master' into feature/androidPort
RednibCoding Aug 2, 2026
cc167af
Fix switch lwl
trulio2 Aug 2, 2026
ea9580c
Merge branch 'master' of https://github.com/RednibCoding/OpenShadowFl…
trulio2 Aug 2, 2026
819550b
Merge branch 'master' of https://github.com/RednibCoding/OpenShadowFl…
trulio2 Aug 2, 2026
f23e8c3
Fix android workflow
trulio2 Aug 2, 2026
ee08644
Merge branch 'master' of https://github.com/RednibCoding/OpenShadowFl…
trulio2 Aug 3, 2026
af541e2
Merge branch 'master' of https://github.com/RednibCoding/OpenShadowFl…
trulio2 Aug 3, 2026
06c2378
Merge branch 'master' of https://github.com/RednibCoding/OpenShadowFl…
trulio2 Aug 3, 2026
89a2010
Merge branch 'master' of https://github.com/RednibCoding/OpenShadowFl…
trulio2 Aug 4, 2026
5b33ae3
Revert android sf_exe build
trulio2 Aug 4, 2026
6e15130
Add android twl and tal backends
trulio2 Aug 4, 2026
7e4638f
Merge branch 'master' of https://github.com/RednibCoding/OpenShadowFl…
trulio2 Aug 4, 2026
011dfdb
Add app icon and fix input
trulio2 Aug 4, 2026
ee6c9d3
Merge branch 'master' of https://github.com/RednibCoding/OpenShadowFl…
trulio2 Aug 4, 2026
3e0a5ba
fix android ram headroom
trulio2 Aug 4, 2026
bc2656e
Merge branch 'master' of https://github.com/RednibCoding/OpenShadowFl…
trulio2 Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Build Android APK

on:
push:
branches:
- master
paths:
- '.github/workflows/android.yml'
- 'CMakeLists.txt'
- 'platform/android/**'
- 'src/**'
- 'thirdparty/**'
- 'tools/android/**'
pull_request:
paths:
- '.github/workflows/android.yml'
- 'CMakeLists.txt'
- 'platform/android/**'
- 'src/**'
- 'thirdparty/**'
- 'tools/android/**'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-24.04

steps:
- name: Check out the repository
uses: actions/checkout@v6

- name: Set up JDK 17
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '17'
cache: gradle

- name: Install Android build dependencies
run: |
SDKMANAGER="$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager"
yes | "$SDKMANAGER" --licenses > /dev/null
"$SDKMANAGER" \
'platforms;android-35' \
'build-tools;35.0.0' \
'ndk;27.0.12077973' \
'cmake;4.1.2'

- name: Set the SDK location for Gradle
run: printf 'sdk.dir=%s\n' "$ANDROID_HOME" > platform/android/local.properties

- name: Build the debug APK
run: sh tools/android/build-apk.sh

- name: Verify the APK
run: test -s build/android/debug/OpenShadowFlare-android-debug.apk

- name: Upload the APK
uses: actions/upload-artifact@v4
with:
name: openshadowflare-android-debug
path: build/android/debug/OpenShadowFlare-android-debug.apk
if-no-files-found: error
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,19 @@ __pycache__/
*.py[cod]

.idea
*.iml
.vs
.vscode
/build/
build*/
tmp/
ShadowEngine/

# Gradle build products
/platform/android/.gradle/
/platform/android/.idea/
/platform/android/local.properties
/platform/android/build/
/platform/android/app/build/
/platform/android/app/.cxx/
/platform/android/app/.externalNativeBuild/
9 changes: 6 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@ set_property(
PROPERTY STRINGS lgl nxfb
)

add_subdirectory(thirdparty/lwl)
add_subdirectory(thirdparty/lal)
if(OPENSHADOWFLARE_BUILD_EXE OR BUILD_TESTING)
add_subdirectory(thirdparty/lwl)
add_subdirectory(thirdparty/lal)
endif()
add_subdirectory(thirdparty/twl)
add_subdirectory(thirdparty/tal)
if(OPENSHADOWFLARE_PRESENTATION_BACKEND STREQUAL "lgl" OR BUILD_TESTING)
if((OPENSHADOWFLARE_BUILD_EXE AND
OPENSHADOWFLARE_PRESENTATION_BACKEND STREQUAL "lgl") OR BUILD_TESTING)
add_subdirectory(thirdparty/lgl)
endif()

Expand Down
73 changes: 73 additions & 0 deletions documentation/android-port.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Android port

OpenShadowFlare runs on Android 8.0 (API 26) or newer as a NativeActivity. It
draws through the TWL backend (EGL + OpenGL ES 2, GPU-scaled to an aspect-fit
rectangle) and plays audio through TAL on AAudio (44.1 kHz stereo). The game is
the native shared library `libmain.so`; there is no Java or Kotlin code.

## What to install

Android Studio is optional. Install a JDK 17 and the Android command-line SDK
tools, then use them to install:

- Android SDK Platform 35
- Build Tools 35.0.0
- NDK 27.0.12077973
- CMake 4.1.2

### Set the SDK location

Gradle reads the SDK path from the untracked
`platform/android/local.properties`. Copy the tracked template and edit the path
for your machine:

```bash
cp platform/android/local.properties.example platform/android/local.properties
```

Windows (forward slashes are fine):

```properties
sdk.dir=C:/Users/<your-user>/AppData/Local/Android/Sdk
```

Linux:

```properties
sdk.dir=/home/<your-user>/Android/Sdk
```

Do not commit this file; it is machine-specific and already in `.gitignore`.

## Build

```bash
sh tools/android/build-apk.sh
```

The installable debug APK is written to:

```text
build/android/debug/OpenShadowFlare-android-debug.apk
```

## Game data

The retail data is not packaged. After installing the APK, copy `ShadowFlare` directory to the app's external files directory so the game finds it at:

```text
/sdcard/Android/data/org.openshadowflare.game/files/ShadowFlare/
```

That folder must hold the retail data at its top level, for example:

```text
ShadowFlare/
├── SFlare.Cfg
├── System/
├── Map/
├── Player/
├── Character/
├── Scenario/
└── Save/
```
40 changes: 40 additions & 0 deletions platform/android/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
plugins {
id 'com.android.application'
}

android {
namespace 'org.openshadowflare.game'
compileSdk 35
ndkVersion '27.0.12077973'

defaultConfig {
applicationId 'org.openshadowflare.game'
minSdk 26
targetSdk 35
versionCode 2
versionName '0.1.0'

ndk {
def osfAbi = project.findProperty('osfAbi')
if (osfAbi) {
abiFilters osfAbi.toString()
} else {
abiFilters 'arm64-v8a', 'x86_64'
}
}

externalNativeBuild {
cmake {
cppFlags '-std=c++17'
arguments '-DBUILD_TESTING=OFF', '-DOPENSHADOWFLARE_BUILD_EXE=OFF'
}
}
}

externalNativeBuild {
cmake {
path file('../../../CMakeLists.txt')
version '4.1.2'
}
}
}
20 changes: 20 additions & 0 deletions platform/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="false"
android:hasCode="false"
android:icon="@mipmap/ic_launcher"
android:label="OpenShadowFlare"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
<activity
android:name="android.app.NativeActivity"
android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:exported="true"
android:screenOrientation="landscape">
<meta-data android:name="android.app.lib_name" android:value="main" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions platform/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
plugins {
id 'com.android.application' version '8.7.3' apply false
}
13 changes: 13 additions & 0 deletions platform/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Reuse a warm Gradle daemon between builds instead of starting a fresh JVM each
# time (the build script no longer passes --no-daemon).
org.gradle.daemon=true

# Run independent work in parallel and reuse task outputs across builds.
org.gradle.parallel=true
org.gradle.caching=true

# Skip the configuration phase when nothing that affects it has changed.
org.gradle.configuration-cache=true

# Give the daemon enough heap to avoid GC pauses on larger builds.
org.gradle.jvmargs=-Xmx2048m
Binary file not shown.
7 changes: 7 additions & 0 deletions platform/android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading