ddnet/scripts/android
Robert Müller 986508e091 Improve Android storage usage, faster launch, remove permissions
Split the user storage location and the data folder in the app specific external storage in the folders `data` and `user` instead of writing the user setting directly to the external storage.

Remove unnecessary storage permissions. The client only accesses files in its own external storage location, hence these permissions are not necessary for Android API 19 and higher, which is always given as we only target API 19 and higher.

Only unpack changed assets when their hash in the integrity index is different instead of unpacking all assets again, so the app starts faster after updates. Avoid unpacking the entire integrity index file unless it changed, by initially reading only the first hash directly from the asset, so the app starts faster when the data is up-to-date.

Add error handling for external storage not being accessible and other I/O errors during unpacking of assets.

Add `android_main.h` header to export the `InitAndroid` function and potentially other functions in the future. The `extern "C"` and `__attribute__((visibility("default")))` attributes seem to be unnecessary, as this function is only called directly from the native code like many other functions without these attributes.

Initialize the Android storage after the loggers, so the log message are printed properly.

Add documentation for the use of `std::exit` on Android, which is used to forcefully terminate the entire process, to ensure that static variables will be initialized correctly when the app is started again after quitting. Returning from the main function is not enough, as this only results in the native thread terminating, but the Java thread will continue. Java does not support unloading libraries once they have been loaded, so all static variables will not have their expected initial values anymore when the app is started again after quitting.

Use `fs_chdir` and `fs_makedir` instead of `chdir` and `mkdir`.
2024-05-25 21:09:42 +02:00
..
files Improve Android storage usage, faster launch, remove permissions 2024-05-25 21:09:42 +02:00
cmake_android.sh Improve Android build script output, improve colors 2024-05-23 22:08:34 +02:00
README.md Improve the Android building README.md 2024-05-21 17:38:48 +02:00

Requirements for building for Android

  • At least 10-15 GiB of free disk space.
  • First follow the general instructions for setting up https://github.com/ddnet/ddnet for building on Linux. This guide has only been tested on Linux.
  • Install the Android NDK (version 26) in the same location where Android Studio would unpack it (~/Android/Sdk/ndk/):
    mkdir ~/Android
    cd ~/Android
    mkdir Sdk
    cd Sdk
    mkdir ndk
    cd ndk
    wget https://dl.google.com/android/repository/android-ndk-r26d-linux.zip
    unzip android-ndk-r26d-linux.zip
    unlink android-ndk-r26d-linux.zip
    
  • Install the Android SDK build tools (version 30.0.3) in the same location where Android Studio would unpack them (~/Android/Sdk/build-tools/):
    # Assuming you already created the Android/Sdk folders in the previous step
    cd ~/Android/Sdk
    mkdir build-tools
    cd build-tools
    wget https://dl.google.com/android/repository/build-tools_r30.0.3-linux.zip
    unzip build-tools_r30.0.3-linux.zip
    unlink build-tools_r30.0.3-linux.zip
    mv android-11 30.0.3
    
  • Install the Android command-line tools and accept the licenses using the SDK manager, otherwise the Gradle build will fail if the licenses have not been accepted:
    # Assuming you already created the Android/Sdk folders in the previous step
    cd ~/Android/Sdk
    mkdir cmdline-tools
    cd cmdline-tools
    wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
    unzip commandlinetools-linux-11076708_latest.zip
    unlink commandlinetools-linux-11076708_latest.zip
    mv cmdline-tools latest
    yes | latest/bin/sdkmanager --licenses
    
  • Install cargo-ndk and add Android targets to rustup to build Rust with the Android NDK:
    cargo install cargo-ndk
    rustup target add armv7-linux-androideabi
    rustup target add i686-linux-android
    rustup target add aarch64-linux-android
    rustup target add x86_64-linux-android
    
  • Install OpenJDK 21:
    sudo apt install openjdk-21-jdk
    
  • Install 7zip for building ddnet-libs:
    sudo apt install p7zip-full
    
  • Install ninja:
    sudo apt install ninja-build
    
  • Install curl:
    sudo apt install curl
    
  • (macOS only) Install coreutils so nproc is available:
    brew install coreutils
    
  • Build the ddnet-libs for Android (see below). Follow all above steps first.

How to build the ddnet-libs for Android

  • There is a script to automatically download and build all repositories, this requires an active internet connection and can take around 30 minutes:
    mkdir build-android-libs
    scripts/compile_libs/gen_libs.sh build-android-libs android
    
    Warning: Do not choose a directory inside the src folder!
  • If you see several red error messages in the first few minutes, abort the compilation with repeated Ctrl+C presses. Examine the output and ensure that you installed the NDK to the correct location.
  • After the script finished executing, it should have created a ddnet-libs directory in your selected output folder, which contains all libraries in the correct directory format and can be merged with the ddnet-libs folder in the source directory:
    cp -r build-android-libs/ddnet-libs/. ddnet-libs/
    

How to build the DDNet client for Android

  • Open a terminal inside the ddnet project root directory and run the following:
    scripts/android/cmake_android.sh <x86/x86_64/arm/arm64/all> <Game name> <Package name> <Debug/Release> <Build folder>
    
    • The first parameter denotes the architecture. Use all to compile for all architectures. Note that all architectures will be compiled in parallel. For testing, only compile for one architecture initially to get readable output.
    • The second parameter denotes the APK name, which must be equal to the library name. If you want to rename the APK, do it after the build.
    • The third parameter denotes the package name of the APK.
    • The fourth parameter denotes the build type.
    • The fifth parameter denotes the build folder.
  • Example to build only for x86_64 architecture in debug mode:
    scripts/android/cmake_android.sh x86_64 DDNet org.ddnet.client Debug build-android-debug
    
  • To build a signed APK, generate a signing key and export environment variables before running the build script:
    keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias
    export TW_KEY_NAME=<key name>
    export TW_KEY_PW=<key password>
    export TW_KEY_ALIAS=<key alias>
    
  • By default, the version code and name of the APK will be determined automatically based on the definitions in src/game/version.h. You can also specify the build version code and name manually before running the build script, e.g.:
    export TW_VERSION_CODE=20210819
    export TW_VERSION_NAME="1.0"
    
    The version code must increase for newer version in order for users to automatically update to them. The version name is the string that will be displayed to the user, e.g. 1.2.3-snapshot4.
  • Example to build a signed APK in release mode for all architectures:
    keytool -genkey -v -keystore Teeworlds.jks -keyalg RSA -keysize 2048 -validity 10000 -alias Teeworlds-Key
    # It will prompt for the password, input for example "mypassword"
    export TW_KEY_NAME=Teeworlds.jks
    export TW_KEY_PW=mypassword
    export TW_KEY_ALIAS=Teeworlds-Key
    # Version code and name will be determined automatically
    scripts/android/cmake_android.sh all DDNet org.ddnet.client Release build-android-release
    
  • Note that you should only generate a signing key once (and make backups). Users can only update apps automatically if the same package name and signing key have been used, else they must manually uninstall the old app.