feat: support building on Android (#3713)

* feat: support building on Android

* chore: add CI for mobile platform
This commit is contained in:
Lucas.Xu 2023-10-19 09:55:23 +08:00 committed by GitHub
parent f60462a853
commit e565d0ee32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 356 additions and 123 deletions

113
.github/workflows/mobile_ci.yaml vendored Normal file
View File

@ -0,0 +1,113 @@
name: Mobile-CI
on:
push:
branches:
- "main"
paths:
- ".github/workflows/mobile_ci.yaml"
- "frontend/**"
- "!frontend/appflowy_tauri/**"
pull_request:
branches:
- "main"
paths:
- ".github/workflows/mobile_ci.yaml"
- "frontend/**"
- "!frontend/appflowy_tauri/**"
env:
FLUTTER_VERSION: "3.10.1"
RUST_TOOLCHAIN: "1.70"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build:
if: github.event.pull_request.draft != true
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
include:
- os: ubuntu-latest
target: aarch64-linux-android
runs-on: ${{ matrix.os }}
steps:
# the following step is required to avoid running out of space
- name: Maximize build space
if: matrix.os == 'ubuntu-latest'
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
- name: Checkout source code
uses: actions/checkout@v2
- name: Install Rust toolchain
id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
target: ${{ matrix.target }}
override: true
profile: minimal
- name: Install flutter
id: flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: "r24"
add-to-path: true
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.os }}
workspaces: |
frontend/rust-lib
- uses: davidB/rust-cargo-make@v1
with:
version: "0.36.6"
- name: Install prerequisites
working-directory: frontend
run: |
rustup target install aarch64-linux-android
rustup target install x86_64-linux-android
cargo install --force duckscript_cli
cargo install cargo-ndk
if [ "$RUNNER_OS" == "Linux" ]; then
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
sudo apt-get install keybinder-3.0 libnotify-dev
sudo apt-get install gcc-multilib
elif [ "$RUNNER_OS" == "Windows" ]; then
vcpkg integrate install
elif [ "$RUNNER_OS" == "macOS" ]; then
echo 'do nothing'
fi
cargo make appflowy-flutter-deps-tools
shell: bash
- name: Build AppFlowy
working-directory: frontend
env:
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
run: |
cargo make --profile development-android appflowy-android-dev

View File

@ -4,6 +4,18 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
// This task only builds the Dart code of AppFlowy.
// It supports both the desktop and mobile version.
"name": "AF: Build Dart Only",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"env": {
"RUST_LOG": "debug",
},
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
// This task builds the Rust and Dart code of AppFlowy.
"name": "AF-desktop: Build All",
@ -17,17 +29,6 @@
},
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
// This task only builds the Dart code of AppFlowy.
"name": "AF-desktop: Build Dart Only",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"env": {
"RUST_LOG": "debug",
},
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
// This task builds will:
// - call the clean task,
@ -65,6 +66,17 @@
},
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
"name": "AF-Android-Simlator: Clean + Rebuild All",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"preLaunchTask": "AF: Clean + Rebuild All (Android Simulator)",
"env": {
"RUST_LOG": "trace"
},
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
"name": "AF-desktop: Debug Rust",
"request": "attach",

View File

@ -84,6 +84,34 @@
"cwd": "${workspaceFolder}"
}
},
{
"label": "AF: Clean + Rebuild All (Android)",
"type": "shell",
"dependsOrder": "sequence",
"dependsOn": [
"AF: Dart Clean",
"AF: Flutter Clean",
"AF: Build Appflowy Core For Android",
"AF: Flutter Pub Get",
"AF: Flutter Package Get",
"AF: Generate Language Files",
"AF: Generate Freezed Files",
"AF: Generate Svg Files"
],
"presentation": {
"reveal": "always",
"panel": "new"
}
},
{
"label": "AF: Build Appflowy Core For Android",
"type": "shell",
"command": "cargo make --profile development-android appflowy-core-dev-android",
"group": "build",
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "AF: Build Appflowy Core",
"type": "shell",

View File

@ -186,6 +186,14 @@ RUST_COMPILE_TARGET = "aarch64-apple-ios"
BUILD_ARCHS = "arm64"
CRATE_TYPE = "staticlib"
[env.development-android]
BUILD_FLAG = "debug"
TARGET_OS = "android"
CRATE_TYPE = "cdylib"
FLUTTER_OUTPUT_DIR = "Debug"
LIB_EXT = "so"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
[tasks.echo_env]
script = ['''
echo "-------- Env Parameters --------"
@ -201,21 +209,6 @@ script = ['''
''']
script_runner = "@shell"
[env.development-android]
BUILD_FLAG = "debug"
TARGET_OS = "android"
CRATE_TYPE = "cdylib"
FLUTTER_OUTPUT_DIR = "Debug"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
[env.production-android]
CARGO_PROFILE = "release"
BUILD_FLAG = "release"
TARGET_OS = "android"
CRATE_TYPE = "cdylib"
FLUTTER_OUTPUT_DIR = "Release"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
[tasks.setup-crate-type]
private = true
script = [

View File

@ -4,8 +4,8 @@
# This file should be version controlled.
version:
revision: 135454af32477f815a7525073027a3ff9eff1bfd
channel: stable
revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
channel: unknown
project_type: app
@ -13,11 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
- platform: windows
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
create_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
base_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
- platform: android
create_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
base_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
# User provided section

View File

@ -11,3 +11,5 @@ GeneratedPluginRegistrant.java
key.properties
**/*.keystore
**/*.jks
.cxx

View File

@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 31
compileSdkVersion 33
ndkVersion "24.0.8215888"
compileOptions {
@ -46,11 +46,16 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "io.appflowy.appflowy"
minSdkVersion 19
minSdkVersion 23
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
externalNativeBuild {
cmake {
arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_STL=c++_shared"
}
}
}
buildTypes {
@ -63,6 +68,14 @@ android {
signingConfig signingConfigs.debug
}
}
namespace 'io.appflowy.appflowy'
externalNativeBuild {
cmake {
path "src/main/CMakeLists.txt"
}
}
}
flutter {

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.appflowy.appflowy">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->

View File

@ -1,42 +1,38 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.appflowy.appflowy">
<application
android:label="appflowy_flutter"
android:icon="@mipmap/ic_launcher"
android:name="${applicationName}">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:label="appflowy_flutter" android:icon="@mipmap/ic_launcher"
android:name="${applicationName}">
<activity android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
<!--
Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI.
-->
<meta-data android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<!--
Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame.
-->
<meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--
Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java
-->
<meta-data android:name="flutterEmbedding" android:value="2" />
</application>
</manifest>

View File

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.10.0)
project(appflowy_flutter)
# arm64-v8a
file(COPY
${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/jniLibs/arm64-v8a
)
# x86_64
file(COPY
${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/x86_64/libc++_shared.so
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/jniLibs/x86_64
)

View File

@ -0,0 +1,18 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
int64_t init_sdk(char *path);
void async_event(int64_t port, const uint8_t *input, uintptr_t len);
const uint8_t *sync_event(const uint8_t *input, uintptr_t len);
int32_t set_stream_port(int64_t port);
void link_me_please(void);
void backend_log(int64_t level, const char *data);
void set_env(const char *data);

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.appflowy.appflowy">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->

View File

@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.8.0'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -24,6 +24,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

@ -2,3 +2,4 @@ org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
org.gradle.caching=true
android.suppressUnsupportedCompileSdk=33

View File

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -170,6 +170,6 @@ SPEC CHECKSUMS:
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
PODFILE CHECKSUM: 8c681999c7764593c94846b2a64b44d86f7a27ac
COCOAPODS: 1.11.3
COCOAPODS: 1.12.1

View File

@ -2,14 +2,14 @@ group 'com.plugin.appflowy_backend'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.8.0'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -25,13 +25,13 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 31
compileSdkVersion 33
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
minSdkVersion 16
minSdkVersion 23
}
}

View File

@ -2,14 +2,14 @@ group 'com.example.flowy_infra_ui'
version '1.0'
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.8.0'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:7.4.2'
}
}
@ -23,7 +23,7 @@ rootProject.allprojects {
apply plugin: 'com.android.library'
android {
compileSdkVersion 30
compileSdkVersion 33
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8

View File

@ -16,4 +16,5 @@ bin/
AppFlowy-Collab/
.env
.env.**
**/unit_test**
**/unit_test**
jniLibs/

View File

@ -72,32 +72,6 @@ script = [
]
script_runner = "@shell"
[tasks.sdk-dev-build-android]
private = true
script = [
"""
cd rust-lib/
rustup show
rustup target add aarch64-linux-android \
armv7-linux-androideabi \
i686-linux-android \
x86_64-linux-android
DEST=${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/appflowy_flutter/android/app/src/main/jniLibs
rm -rf $DEST/arm64-v8a \
$DEST/armeabi-v7a \
$DEST/x86 \
$DEST/x86_64
cargo ndk \
-t arm64-v8a \
-t armeabi-v7a \
-t x86 \
-t x86_64 \
-o $DEST build
cd ../
""",
]
script_runner = "@shell"
[tasks.sdk-dev-build.windows]
private = true
script = [
@ -192,4 +166,3 @@ script = [
""",
]
script_runner = "@duckscript"

View File

@ -52,7 +52,16 @@ dependencies = ["appflowy-core-dev-ios"]
run_task = { name = [
"code_generation",
"set-app-version",
"flutter-build",
"flutter-build-ios",
] }
script_runner = "@shell"
[tasks.appflowy-android-dev]
dependencies = ["appflowy-core-dev-android"]
run_task = { name = [
"code_generation",
"set-app-version",
"flutter-build-android",
] }
script_runner = "@shell"
@ -187,6 +196,22 @@ script = ["""
"""]
script_runner = "@shell"
[tasks.flutter-build-ios]
script = ["""
cd appflowy_flutter/
flutter pub get
flutter build ipa --verbose
"""]
script_runner = "@shell"
[tasks.flutter-build-android]
script = ["""
cd appflowy_flutter/
flutter pub get
flutter build apk --split-per-abi --verbose
"""]
script_runner = "@shell"
[tasks.flutter-build.windows]
script = ["""
cd appflowy_flutter

View File

@ -33,6 +33,29 @@ script = [
]
script_runner = "@shell"
[tasks.appflowy-core-dev-android]
category = "Build"
dependencies = ["env_check"]
run_task = { name = [
"setup-crate-type",
"sdk-build-android",
"post-mobile-android",
"restore-crate-type",
] }
[tasks.sdk-build-android]
private = true
script = [
"""
cd rust-lib/
rustup show
echo "cargo ndk -t arm64-v8a -t x86_64 -o ./jniLibs build --features "${FLUTTER_DESKTOP_FEATURES}""
cargo ndk -t arm64-v8a -t x86_64 -o ./jniLibs build --features "${FLUTTER_DESKTOP_FEATURES}"
cd ../
""",
]
script_runner = "@shell"
[tasks.post-mobile-ios]
private = true
script = [
@ -53,3 +76,23 @@ script = [
""",
]
script_runner = "@duckscript"
[tasks.post-mobile-android]
script = [
"""
echo "🚀 🚀 🚀 AppFlowy-Core for Android platform build success"
dart_ffi_dir= set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/appflowy_flutter/android/app/src/main
lib = set lib${LIB_NAME}.${LIB_EXT}
echo "💻 💻 💻 Copying ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/jniLibs/ to ${dart_ffi_dir}/"
rm -r ${dart_ffi_dir}/jniLibs/
cp ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/jniLibs/ \
${dart_ffi_dir}/
echo "💻 💻 💻 Copying ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/binding.h to ${dart_ffi_dir}/Classes/binding.h"
rm -f ${dart_ffi_dir}/Classes/binding.h
cp ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/binding.h \
${dart_ffi_dir}/Classes/binding.h
""",
]
script_runner = "@duckscript"