Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
21 changes: 21 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,27 @@ jobs:
- name: Build usecases demo
run: cargo apk build -p usecases --target aarch64-linux-android --lib

android_cpp:
needs: files-changed
if: ${{ needs.files-changed.outputs.internal == 'true' || needs.files-changed.outputs.api_cpp == 'true' || needs.files-changed.outputs.examples == 'true' }}
runs-on: ubuntu-latest
env:
CARGO_INCREMENTAL: false
CARGO_PROFILE_DEV_DEBUG: 0
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/install-linux-dependencies
- uses: ./.github/actions/setup-rust
with:
target: aarch64-linux-android
- name: Build Slint and usecases demo for Android (CMake cross-compile)
run: |
cmake -B build-android -S . \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_LATEST_HOME/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-26 \
-DSLINT_BUILD_EXAMPLES=ON
cmake --build build-android --target usecases

test-figma-inspector:
needs: files-changed
if: ${{ needs.files-changed.outputs.figma_inspector == 'true' }}
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,15 @@ path = ["examples/bevy/bevy-hosts-slint/**.gltf"]
precedence = "aggregate"
SPDX-FileCopyrightText = "Copyright (c) 2024 e-verse"
SPDX-License-Identifier = "MIT"

[[annotations]]
path = ["api/cpp/android/**/gradle-wrapper.properties", "examples/**/android/**/gradle-wrapper.properties"]
Comment thread
ruminorix marked this conversation as resolved.
Outdated
precedence = "aggregate"
SPDX-FileCopyrightText = "Copyright © SixtyFPS GmbH <info@slint.dev>"
SPDX-License-Identifier = "GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0"

[[annotations]]
path = ["demos/**/android/**/gradle-wrapper.properties"]
precedence = "aggregate"
SPDX-FileCopyrightText = "Copyright © SixtyFPS GmbH <info@slint.dev>"
SPDX-License-Identifier = "MIT"
71 changes: 71 additions & 0 deletions api/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,40 @@ endif ()
list(PREPEND CMAKE_MODULE_PATH ${Corrosion_SOURCE_DIR}/cmake)
find_package(Rust 1.92 REQUIRED MODULE)

# When building for Android (NDK toolchain sets CMAKE_SYSTEM_NAME to "Android"),
# auto-configure the Rust target, enable the android backend, and set sensible defaults.
# This block must run before the feature options are defined so that the forced cache values
# are picked up by define_cargo_feature / define_cargo_dependent_feature.
if(ANDROID)
# Map Android ABI to Rust target triple
if(ANDROID_ABI STREQUAL "arm64-v8a")
set(Rust_CARGO_TARGET "aarch64-linux-android" CACHE STRING "" FORCE)
elseif(ANDROID_ABI STREQUAL "armeabi-v7a")
set(Rust_CARGO_TARGET "armv7-linux-androideabi" CACHE STRING "" FORCE)
elseif(ANDROID_ABI STREQUAL "x86_64")
set(Rust_CARGO_TARGET "x86_64-linux-android" CACHE STRING "" FORCE)
elseif(ANDROID_ABI STREQUAL "x86")
set(Rust_CARGO_TARGET "i686-linux-android" CACHE STRING "" FORCE)
else()
message(FATAL_ERROR "Unsupported ANDROID_ABI: ${ANDROID_ABI}")
endif()

# Android requires a shared library
set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if these defaults could be set at the source instead of here with these kind of ugly overrides. We're in the right file, aren't we?:)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Although these are not default but mandatory features

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the comment to say "Android requires the skia renderer and cannot use desktop backends".

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could integrate it into the define_cargo_dependent_feature calls by adding AND NOT ANDROID to the depends_condition for winit/qt/femtovg, and using if(ANDROID) to flip the renderer-skia default to ON. The issue is that these are mandatory for Android, not just defaults — with the current approach, FORCE prevents the user from accidentally enabling winit on Android. With the depends_condition approach, we get the same effect since cmake_dependent_option forces the value OFF when the condition is false.

Want me to try that approach? It would replace the FORCE overrides with conditions on the feature definitions themselves.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If mandatory perhaps that should become an option in the macro/function. But IMO it's sufficient if defaults are on/off correctly and it's all in one place.

My concern is that if we remove/rename the feature we need to look at multiple places.

(Not a super big issue but worth research if it can be done better)


# Enable the android backend and skia renderer, disable desktop backends
set(SLINT_FEATURE_BACKEND_ANDROID_ACTIVITY ON CACHE BOOL "" FORCE)
set(SLINT_FEATURE_RENDERER_SKIA ON CACHE BOOL "" FORCE)
set(SLINT_FEATURE_BACKEND_WINIT OFF CACHE BOOL "" FORCE)
set(SLINT_FEATURE_BACKEND_QT OFF CACHE BOOL "" FORCE)
set(SLINT_FEATURE_RENDERER_FEMTOVG OFF CACHE BOOL "" FORCE)

Comment thread
ruminorix marked this conversation as resolved.
Outdated
# Default to Material style on Android
if(NOT SLINT_STYLE)
set(SLINT_STYLE "material" CACHE STRING "" FORCE)
endif()
endif()

option(BUILD_SHARED_LIBS "Build Slint as shared library" ON)
option(SLINT_FEATURE_COMPILER "Enable support for compiling .slint files to C++ ahead of time" ON)
add_feature_info(SLINT_FEATURE_COMPILER SLINT_FEATURE_COMPILER "Enable support for compiling .slint files to C++ ahead of time")
Expand Down Expand Up @@ -111,6 +145,8 @@ define_cargo_dependent_feature(backend-qt "Enable Qt based rendering backend" OF
define_cargo_dependent_feature(backend-linuxkms "Enable support for the backend that renders a single window fullscreen on Linux. Requires libseat. If you don't have libseat, select `backend-linuxkms-noseat` instead." OFF "NOT SLINT_FEATURE_FREESTANDING")
define_cargo_dependent_feature(backend-linuxkms-noseat "Enable support for the backend that renders a single window fullscreen on Linux" OFF "NOT SLINT_FEATURE_FREESTANDING")

define_cargo_dependent_feature(backend-android-activity "Enable the Android backend for building Slint apps on Android" OFF "NOT SLINT_FEATURE_FREESTANDING")

define_cargo_dependent_feature(gettext "Enable support of translations using gettext" OFF "NOT SLINT_FEATURE_FREESTANDING")
define_cargo_dependent_feature(accessibility "Enable integration with operating system provided accessibility APIs" ON "NOT SLINT_FEATURE_FREESTANDING")
define_cargo_dependent_feature(testing "Enable support for testing API (experimental)" ON "NOT SLINT_FEATURE_FREESTANDING")
Expand Down Expand Up @@ -306,6 +342,41 @@ if (SLINT_BUILD_RUNTIME)
)
endif()

# Propagate Android NDK/SDK paths to the Rust build so that the android-activity
# backend can compile Java sources and produce DEX bytecode.
if(ANDROID)
if(DEFINED ENV{ANDROID_NDK_ROOT})
set_property(
TARGET slint_cpp
APPEND
PROPERTY CORROSION_ENVIRONMENT_VARIABLES
"ANDROID_NDK_ROOT=$ENV{ANDROID_NDK_ROOT}"
)
elseif(CMAKE_ANDROID_NDK)
set_property(
TARGET slint_cpp
APPEND
PROPERTY CORROSION_ENVIRONMENT_VARIABLES
"ANDROID_NDK_ROOT=${CMAKE_ANDROID_NDK}"
)
endif()
if(DEFINED ENV{ANDROID_HOME})
set_property(
TARGET slint_cpp
APPEND
PROPERTY CORROSION_ENVIRONMENT_VARIABLES
"ANDROID_HOME=$ENV{ANDROID_HOME}"
)
elseif(CMAKE_ANDROID_SDK)
set_property(
TARGET slint_cpp
APPEND
PROPERTY CORROSION_ENVIRONMENT_VARIABLES
"ANDROID_HOME=${CMAKE_ANDROID_SDK}"
)
endif()
endif()

if(SLINT_FEATURE_RENDERER_SKIA OR SLINT_FEATURE_RENDERER_SKIA_OPENGL OR SLINT_FEATURE_RENDERER_SKIA_VULKAN)
find_program(CLANGCC clang)
find_program(CLANGCXX clang++)
Expand Down
9 changes: 9 additions & 0 deletions api/cpp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ backend-winit-x11 = ["i-slint-backend-selector/backend-winit-x11", "std"]
backend-winit-wayland = ["i-slint-backend-selector/backend-winit-wayland", "std"]
backend-linuxkms = ["i-slint-backend-selector/backend-linuxkms", "std"]
backend-linuxkms-noseat = ["i-slint-backend-selector/backend-linuxkms-noseat", "std"]
# Auto-enabled by CMake when building for Android; not intended as a user-facing option.
backend-android-activity = [
Comment thread
ruminorix marked this conversation as resolved.
Outdated
"i-slint-backend-android-activity/native-activity",
"i-slint-backend-android-activity/aa-06",
"i-slint-backend-selector/backend-android-activity",
"renderer-skia",
"std",
]
renderer-femtovg = ["i-slint-backend-selector/renderer-femtovg"]
renderer-femtovg-wgpu = ["i-slint-backend-selector/renderer-femtovg-wgpu"]
renderer-skia = ["i-slint-backend-selector/renderer-skia", "i-slint-renderer-skia", "raw-window-handle"]
Expand Down Expand Up @@ -69,6 +77,7 @@ i-slint-renderer-software = { workspace = true, optional = true }
i-slint-core = { workspace = true, features = ["ffi"] }
slint-interpreter = { workspace = true, features = ["ffi", "compat-1-2"], optional = true }
raw-window-handle = { version = "0.6", optional = true }
i-slint-backend-android-activity = { workspace = true, optional = true }

esp-backtrace = { version = "0.17.0", features = ["panic-handler", "println"], optional = true }
esp-println = { version = "0.15.0", default-features = false, features = ["auto", "log-04"], optional = true }
Expand Down
38 changes: 38 additions & 0 deletions api/cpp/android/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Slint C++ Android Template
Comment thread
ruminorix marked this conversation as resolved.
Outdated

This directory contains a template project for building Slint C++ applications
on Android using Gradle and CMake.

See the [Slint C++ Android documentation](https://slint.dev/docs/guide/platforms/mobile/android-cpp)
for detailed instructions.

## Quick Start

1. Copy this directory to create your project.

2. Set up the required environment variables:
```sh
export ANDROID_HOME=$HOME/Android/Sdk
export ANDROID_NDK_ROOT=$ANDROID_HOME/ndk/<version>
```

3. Install the Rust Android target:
```sh
rustup target add aarch64-linux-android
```

4. Edit `app/src/main/cpp/main.cpp` and `app/src/main/cpp/main.slint` with
your application code.

5. Build and deploy:
```sh
./gradlew installDebug
```

## Project Structure

- `app/build.gradle.kts` - Android app configuration (SDK versions, CMake setup)
- `app/src/main/AndroidManifest.xml` - Android manifest with NativeActivity
- `app/src/main/cpp/CMakeLists.txt` - CMake build for C++ code and Slint
- `app/src/main/cpp/main.cpp` - Application entry point (`slint_main`)
- `app/src/main/cpp/main.slint` - Slint UI definition
38 changes: 38 additions & 0 deletions api/cpp/android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

plugins {
id("com.android.application")
}

android {
namespace = "dev.slint.app"
compileSdk = 35

defaultConfig {
applicationId = "dev.slint.app"
// API 26 required for InMemoryDexClassLoader used by the Slint Android backend
minSdk = 26
targetSdk = 35
versionCode = 1
versionName = "1.0"

ndk {
// Add other ABIs as needed: "armeabi-v7a", "x86_64", "x86"
abiFilters += "arm64-v8a"
}
}

buildTypes {
release {
isMinifyEnabled = false
}
}

externalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
}
22 changes: 22 additions & 0 deletions api/cpp/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> -->
<!-- SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 -->

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="Slint App"
android:hasCode="false">
<activity
android:name="android.app.NativeActivity"
android:exported="true"
android:configChanges="orientation|screenSize|keyboardHidden">
<meta-data
android:name="android.app.lib_name"
android:value="app" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
18 changes: 18 additions & 0 deletions api/cpp/android/app/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright © SixtyFPS GmbH <info@slint.dev>
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

cmake_minimum_required(VERSION 3.21)
project(SlintApp LANGUAGES CXX)

include(FetchContent)
FetchContent_Declare(
Slint
GIT_REPOSITORY https://github.com/slint-ui/slint.git
GIT_TAG v1.17.0
SOURCE_SUBDIR api/cpp
)
FetchContent_MakeAvailable(Slint)

add_library(app SHARED main.cpp)
target_link_libraries(app PRIVATE Slint::Slint)
slint_target_sources(app main.slint)
13 changes: 13 additions & 0 deletions api/cpp/android/app/src/main/cpp/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

#include "main.h" // Generated from main.slint

// Entry point for Slint applications on Android.
// The Slint runtime takes care of Android platform initialization;
// this function is called once the platform is ready.
extern "C" void slint_main()
{
auto window = MainWindow::create();
window->run();
}
17 changes: 17 additions & 0 deletions api/cpp/android/app/src/main/cpp/main.slint
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

export component MainWindow inherits Window {
preferred-width: 360px;
preferred-height: 640px;

VerticalLayout {
alignment: center;

Text {
text: "Hello from Slint on Android!";
horizontal-alignment: center;
font-size: 24px;
}
}
}
6 changes: 6 additions & 0 deletions api/cpp/android/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

plugins {
id("com.android.application") version "8.7.0" apply false
}
4 changes: 4 additions & 0 deletions api/cpp/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright © SixtyFPS GmbH <info@slint.dev>
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

android.useAndroidX=true
8 changes: 8 additions & 0 deletions api/cpp/android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright © SixtyFPS GmbH <info@slint.dev>
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
20 changes: 20 additions & 0 deletions api/cpp/android/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}

dependencyResolution {
repositories {
google()
mavenCentral()
}
}

rootProject.name = "SlintApp"
include(":app")
2 changes: 2 additions & 0 deletions api/cpp/cbindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,7 @@ namespace slint {
using types::IntRect;
using types::Size;
using types::MouseEvent;
using ::slint::Timer;
Comment thread
ruminorix marked this conversation as resolved.
Outdated
}
template<typename ModelData> class Model;
}",
Expand Down Expand Up @@ -1051,6 +1052,7 @@ declare_features! {
backend_winit_wayland
backend_linuxkms
backend_linuxkms_noseat
backend_android_activity
renderer_femtovg
renderer_skia
renderer_skia_opengl
Expand Down
Loading
Loading