Skip to content

Commit

Permalink
Android: CI changes for Play Store
Browse files Browse the repository at this point in the history
* android build conf changes

* fix shellcheck warnings

android fix audio permission handling

android fix androidextras include

android remove redundant jdk install

add ANDROID_NDK_ROOT comment

fix for clang-format style check

android add all ABI support

android: remove redundant minSdkVersion

clang-format fixes

android: fix manifest xml, remove old boilerplate

add ANDROID_SDK_ROOT shellcheck fix

android fix assignments in pro file

fix typo
  • Loading branch information
danryu authored and pljones committed Dec 20, 2024
1 parent 8f50123 commit 17b339a
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 107 deletions.
245 changes: 169 additions & 76 deletions .github/autobuild/android.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,51 @@

set -eu

# Some of the following version pinnings are semi-automatically checked for
# updates. Update .github/workflows/bump-dependencies.yaml when renaming those:
COMMANDLINETOOLS_VERSION=6858069
ANDROID_NDK_VERSION=r21d
ANDROID_PLATFORM=android-30
ANDROID_BUILD_TOOLS=30.0.2
AQTINSTALL_VERSION=3.1.18
QT_VERSION=5.15.2

# Only variables which are really needed by sub-commands are exported.
# Definitions have to stay in a specific order due to dependencies.
## TODO: Decide whether we want to use this action.
## TODO: The patch not only adopts the new action but changes dependencies:
## TODO: - COMMANDLINETOOLS_VERSION from 6858069 to 7.0
## TODO: - ANDROID_NDK_VERSION from r21d to 25.1.8937393
## TODO: - ANDROID_PLATFORM from android-30 to android-33
## TODO: - ANDROID_BUILD_TOOLS from 30.0.2 to 33.0.0
## TODO: - AQTINSTALL_VERSION from 3.1.18 to 2.1.0
## TODO: - QT_VERSION from 5.15.2 to 6.3.2

# # Some of the following version pinnings are semi-automatically checked for
# # updates. Update .github/workflows/bump-dependencies.yaml when renaming those:
# COMMANDLINETOOLS_VERSION=6858069
# ANDROID_NDK_VERSION=r21d
# ANDROID_PLATFORM=android-30
# ANDROID_BUILD_TOOLS=30.0.2
# AQTINSTALL_VERSION=3.1.18
# QT_VERSION=5.15.2

## From https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md
# Tools already installed:
# Android Command Line Tools 7.0
# Android SDK Build-tools 33.0.0
# Android SDK Platform-Tools 33.0.3
# Android SDK Platforms android-33 (rev 2)
# Android SDK Tools 26.1.1

# Env vars set:
# ANDROID_HOME /usr/local/lib/android/sdk
# ANDROID_NDK /usr/local/lib/android/sdk/ndk/25.1.8937393
# ANDROID_NDK_HOME /usr/local/lib/android/sdk/ndk/25.1.8937393
# ANDROID_NDK_LATEST_HOME /usr/local/lib/android/sdk/ndk/25.1.8937393
# ANDROID_NDK_ROOT /usr/local/lib/android/sdk/ndk/25.1.8937393
# ANDROID_SDK_ROOT /usr/local/lib/android/sdk

ANDROID_PLATFORM=android-33
AQTINSTALL_VERSION=2.1.0
QT_VERSION=6.3.2
QT_BASEDIR="/opt/Qt"
ANDROID_BASEDIR="/opt/android"
BUILD_DIR=build
export ANDROID_SDK_ROOT="${ANDROID_BASEDIR}/android-sdk"
COMMANDLINETOOLS_DIR="${ANDROID_SDK_ROOT}"/cmdline-tools/latest/
export ANDROID_NDK_ROOT="${ANDROID_BASEDIR}/android-ndk"
ANDROID_NDK_HOST="linux-x86_64"
ANDROID_SDKMANAGER="${COMMANDLINETOOLS_DIR}/bin/sdkmanager"
export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64/"
# Github CI image-provided env vars, but explicitly re-assign to placate shellcheck
ANDROID_NDK_ROOT="${ANDROID_NDK_ROOT:?ANDROID_NDK_ROOT should be provided}"
ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT:?ANDROID_SDK_ROOT should be provided}"
# Only variables which are really needed by sub-commands are exported.
export JAVA_HOME=${JAVA_HOME_11_X64}
export PATH="${PATH}:${ANDROID_SDK_ROOT}/tools"
export PATH="${PATH}:${ANDROID_SDK_ROOT}/platform-tools"

Expand All @@ -58,36 +83,7 @@ setup_ubuntu_dependencies() {
export DEBIAN_FRONTEND="noninteractive"

sudo apt-get -qq update
sudo apt-get -qq --no-install-recommends -y install build-essential zip unzip bzip2 p7zip-full curl chrpath openjdk-8-jdk-headless
}

setup_android_sdk() {
mkdir -p "${ANDROID_BASEDIR}"

if [[ -d "${COMMANDLINETOOLS_DIR}" ]]; then
echo "Using commandlinetools installation from previous run (actions/cache)"
else
mkdir -p "${COMMANDLINETOOLS_DIR}"
curl -s -o downloadfile "https://dl.google.com/android/repository/commandlinetools-linux-${COMMANDLINETOOLS_VERSION}_latest.zip"
unzip -q downloadfile
mv cmdline-tools/* "${COMMANDLINETOOLS_DIR}"
fi

yes | "${ANDROID_SDKMANAGER}" --licenses
"${ANDROID_SDKMANAGER}" --update
"${ANDROID_SDKMANAGER}" "platforms;${ANDROID_PLATFORM}"
"${ANDROID_SDKMANAGER}" "build-tools;${ANDROID_BUILD_TOOLS}"
}

setup_android_ndk() {
mkdir -p "${ANDROID_BASEDIR}"
if [[ -d "${ANDROID_NDK_ROOT}" ]]; then
echo "Using NDK installation from previous run (actions/cache)"
else
curl -s -o downloadfile "https://dl.google.com/android/repository/android-ndk-${ANDROID_NDK_VERSION}-linux-x86_64.zip"
unzip -q downloadfile
mv "android-ndk-${ANDROID_NDK_VERSION}" "${ANDROID_NDK_ROOT}"
fi
sudo apt-get -qq --no-install-recommends -y install build-essential zip unzip bzip2 p7zip-full curl chrpath
}

setup_qt() {
Expand All @@ -96,54 +92,151 @@ setup_qt() {
else
echo "Installing Qt..."
python3 -m pip install "aqtinstall==${AQTINSTALL_VERSION}"
local qtmultimedia=()
if [[ ! "${QT_VERSION}" =~ 5\..* ]]; then
# From Qt6 onwards, qtmultimedia is a module and cannot be installed
# as an archive anymore.
qtmultimedia=("--modules")
fi
qtmultimedia+=("qtmultimedia")

python3 -m aqt install-qt --outputdir "${QT_BASEDIR}" linux android "${QT_VERSION}" \
--archives qtbase qttools qttranslations qtandroidextras \
"${qtmultimedia[@]}"
# Delete libraries, which we don't use, but which bloat the resulting package and might introduce unwanted dependencies.
find "${QT_BASEDIR}" -name 'libQt5*Quick*.so' -delete
rm -r "${QT_BASEDIR}/${QT_VERSION}/android/qml/"
# icu needs explicit installation
# otherwise: "qmake: error while loading shared libraries: libicui18n.so.56: cannot open shared object file: No such file or directory"
python3 -m aqt install-qt --outputdir "${QT_BASEDIR}" linux desktop "${QT_VERSION}" \
--archives qtbase qtdeclarative qtsvg qttools icu \
--modules qtmultimedia

# - 64bit required for Play Store
python3 -m aqt install-qt --outputdir "${QT_BASEDIR}" linux android "${QT_VERSION}" android_arm64_v8a \
--archives qtbase qtdeclarative qtsvg qttools \
--modules qtmultimedia

# Also install for arm_v7 to build for 32bit devices
python3 -m aqt install-qt --outputdir "${QT_BASEDIR}" linux android "${QT_VERSION}" android_armv7 \
--archives qtbase qtdeclarative qtsvg qttools \
--modules qtmultimedia

fi
}

build_app_as_apk() {
local QT_DIR="${QT_BASEDIR}/${QT_VERSION}/android"
build_app() {
local ARCH_ABI="${1}"

local MAKE="${ANDROID_NDK_ROOT}/prebuilt/${ANDROID_NDK_HOST}/bin/make"

"${QT_DIR}/bin/qmake" -spec android-clang
echo "${GOOGLE_RELEASE_KEYSTORE}" | base64 --decode > android/android_release.keystore

echo ">>> Compiling for ${ARCH_ABI} ..."

# Override ANDROID_ABIS according to build target
# note: seems ANDROID_ABIS can be set here at cmdline, but ANDROID_VERSION_CODE cannot - must be in qmake file
if [ "${ARCH_ABI}" == "android_armv7" ]; then
echo ">>> Running qmake with ANDROID_ABIS=armeabi-v7a ..."
ANDROID_ABIS=armeabi-v7a \
"${QT_BASEDIR}/${QT_VERSION}/${ARCH_ABI}/bin/qmake" -spec android-clang
elif [ "${ARCH_ABI}" == "android_arm64_v8a" ]; then
echo ">>> Running qmake with ANDROID_ABIS=arm64-v8a ..."
ANDROID_ABIS=arm64-v8a \
"${QT_BASEDIR}/${QT_VERSION}/${ARCH_ABI}/bin/qmake" -spec android-clang
elif [ "${ARCH_ABI}" == "android_x86" ]; then
echo ">>> Running qmake with ANDROID_ABIS=arm64-v8a ..."
ANDROID_ABIS=x86 \
"${QT_BASEDIR}/${QT_VERSION}/${ARCH_ABI}/bin/qmake" -spec android-clang
elif [ "${ARCH_ABI}" == "android_x86_64" ]; then
echo ">>> Running qmake with ANDROID_ABIS=arm64-v8a ..."
ANDROID_ABIS=x86_64 \
"${QT_BASEDIR}/${QT_VERSION}/${ARCH_ABI}/bin/qmake" -spec android-clang
fi
"${MAKE}" -j "$(nproc)"
"${MAKE}" INSTALL_ROOT="${BUILD_DIR}" -f Makefile install
"${QT_DIR}"/bin/androiddeployqt --input android-Jamulus-deployment-settings.json --output "${BUILD_DIR}" \
--android-platform "${ANDROID_PLATFORM}" --jdk "${JAVA_HOME}" --gradle
"${MAKE}" INSTALL_ROOT="${BUILD_DIR}_${ARCH_ABI}" -f Makefile install
}

build_make_clean() {
echo ">>> Doing make clean ..."
local MAKE="${ANDROID_NDK_ROOT}/prebuilt/${ANDROID_NDK_HOST}/bin/make"
"${MAKE}" clean
rm -f Makefile
}

build_aab() {
local ARCH_ABI="${1}"

if [ "${ARCH_ABI}" == "android_armv7" ]; then
TARGET_ABI=armeabi-v7a
elif [ "${ARCH_ABI}" == "android_arm64_v8a" ]; then
TARGET_ABI=arm64-v8a
elif [ "${ARCH_ABI}" == "android_x86" ]; then
TARGET_ABI=x86
elif [ "${ARCH_ABI}" == "android_x86_64" ]; then
TARGET_ABI=x86_64
fi
echo ">>> Building .aab file for ${TARGET_ABI}...."

ANDROID_ABIS=${TARGET_ABI} ${QT_BASEDIR}/${QT_VERSION}/gcc_64/bin/androiddeployqt --input android-Jamulus-deployment-settings.json \
--verbose \
--output "${BUILD_DIR}_${ARCH_ABI}" \
--aab \
--release \
--sign android/android_release.keystore jamulus \
--storepass "${GOOGLE_KEYSTORE_PASS}" \
--android-platform "${ANDROID_PLATFORM}" \
--jdk "${JAVA_HOME}" \
--gradle
}

pass_artifact_to_job() {
mkdir deploy
local artifact="jamulus_${JAMULUS_BUILD_VERSION}_android.apk"
echo "Moving ${BUILD_DIR}/build/outputs/apk/debug/build-debug.apk to deploy/${artifact}"
mv "./${BUILD_DIR}/build/outputs/apk/debug/build-debug.apk" "./deploy/${artifact}"
echo "artifact_1=${artifact}" >> "$GITHUB_OUTPUT"
local ARCH_ABI="${1}"
echo ">>> Deploying .aab file for ${ARCH_ABI}...."

if [ "${ARCH_ABI}" == "android_armv7" ]; then
NUM="1"
BUILDNAME="arm"
elif [ "${ARCH_ABI}" == "android_arm64_v8a" ]; then
NUM="2"
BUILDNAME="arm64"
elif [ "${ARCH_ABI}" == "android_x86" ]; then
NUM="3"
BUILDNAME="x86"
elif [ "${ARCH_ABI}" == "android_x86_64" ]; then
NUM="4"
BUILDNAME="x86_64"
fi

#mkdir deploy
#local artifact="jamulus_${JAMULUS_BUILD_VERSION}_android.apk"
mkdir -p deploy
local artifact="Jamulus_${JAMULUS_BUILD_VERSION}_android_${BUILDNAME}.aab"
# debug to check for filenames
ls -alR "${BUILD_DIR}_${ARCH_ABI}/build/outputs/bundle/release/"
ls -al "${BUILD_DIR}_${ARCH_ABI}/build/outputs/bundle/release/build_${ARCH_ABI}-release.aab"

#echo "Moving ${BUILD_DIR}/build/outputs/apk/debug/build-debug.apk to deploy/${artifact}"
#mv "./${BUILD_DIR}/build/outputs/apk/debug/build-debug.apk" "./deploy/${artifact}"
echo ">>> Moving ${BUILD_DIR}_${ARCH_ABI}/build/outputs/bundle/release/build_${ARCH_ABI}-release.aab to deploy/${artifact}"
mv "./${BUILD_DIR}_${ARCH_ABI}/build/outputs/bundle/release/build_${ARCH_ABI}-release.aab" "./deploy/${artifact}"
echo ">>> Moved .aab file to deploy/${artifact}"
echo ">>> Artifact number is: ${NUM}"
echo ">>> Setting output as such: name=artifact_${NUM}::${artifact}"
#echo "artifact_1=${artifact}" >> "$GITHUB_OUTPUT"
echo "::set-output name=artifact_${NUM}::${artifact}"
}

case "${1:-}" in
setup)
setup_ubuntu_dependencies
setup_android_ndk
setup_android_sdk
setup_qt
;;
build)
build_app_as_apk
# Build all targets in sequence
build_app "android_armv7"
build_aab "android_armv7"
build_make_clean
build_app "android_arm64_v8a"
build_aab "android_arm64_v8a"
build_make_clean
build_app "android_x86"
build_aab "android_x86"
build_make_clean
build_app "android_x86_64"
build_aab "android_x86_64"
;;
get-artifacts)
pass_artifact_to_job
pass_artifact_to_job "android_armv7"
pass_artifact_to_job "android_arm64_v8a"
pass_artifact_to_job "android_x86"
pass_artifact_to_job "android_x86_64"
;;
*)
echo "Unknown stage '${1:-}'"
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/autobuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ jobs:
NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
MACOS_CA_PUBLICKEY: ${{ secrets.MACOS_CA_PUBKEY }}
GOOGLE_RELEASE_KEYSTORE: ${{ secrets.GOOGLE_KEYSTORE }}
GOOGLE_KEYSTORE_PASS: ${{ secrets.GOOGLE_KEYSTORE_PASS }}

- name: Post-Build for ${{ matrix.config.config_name }}
id: get-artifacts
run: ${{ matrix.config.base_command }} get-artifacts
Expand Down Expand Up @@ -427,6 +430,25 @@ jobs:
JAMULUS_BUILD_VERSION: ${{ needs.create_release.outputs.build_version }}
ARTIFACT_PATH: deploy/${{ steps.get-artifacts.outputs.artifact_1 }}

## RELEASE PROCEDURE FOR:
## - Android Play Store - aab
## Requirement: Service Account JSON setup:
## - Google Play Console -> Setup -> API Access -> Create/Link Google Cloud Project
## - Google Cloud console -> IAM & Admin -> Service Accounts -> Create (Wizard). Then create JSON key and export/save
- name: Publish all Android ABI builds to Play Store
if: >-
needs.create_release.outputs.publish_to_release == 'true' &&
matrix.config.target_os == 'android'
id: publish_android
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }}
packageName: io.jamulus.jamulus
releaseFiles: deploy/Jamulus*.aab
releaseName: ${{ needs.create_release.outputs.build_version }}
track: beta
status: draft

- name: Deploy Artifact 1 to Release
if: needs.create_release.outputs.publish_to_release == 'true'
id: upload-release-asset1
Expand Down
43 changes: 39 additions & 4 deletions Jamulus.pro
Original file line number Diff line number Diff line change
Expand Up @@ -244,18 +244,53 @@ win32 {
LIBS += -framework AVFoundation \
-framework AudioToolbox
} else:android {
ANDROID_ABIS = armeabi-v7a arm64-v8a x86 x86_64
# By default build for all the ABIs
# ANDROID_ABIS = armeabi-v7a arm64-v8a x86 x86_64

# get ANDROID_ABIS from environment - passed directly to qmake
ANDROID_ABIS = $$getenv(ANDROID_ABIS)

# Optional: if ANDROID_ABIS is passed as env var to qmake, will override this
# !defined(ANDROID_ABIS, var):ANDROID_ABIS = arm64-v8a

# Experiments show likely better results with Android 10+ devices
ANDROID_MIN_SDK_VERSION = 29
ANDROID_TARGET_SDK_VERSION = 32
ANDROID_VERSION_NAME = $$VERSION
ANDROID_VERSION_CODE = $$system(git log --oneline | wc -l)

## For local Dev use on Windows/WSA:
equals(QMAKE_HOST.os, Windows) {
ANDROID_ABIS = x86_64
ANDROID_VERSION_CODE = 1234 # dummy int value
} else {
# date-based unique integer value for Play Store submission
!defined(ANDROID_VERSION_CODE, var):ANDROID_VERSION_CODE = $$system(date +%s | cut -c 2-)
}

# make separate version codes for each abi build otherwise Play Store rejects
contains (ANDROID_ABIS, armeabi-v7a) {
ANDROID_VERSION_CODE = $$num_add($$ANDROID_VERSION_CODE, 1)
message("Setting for armeabi-v7a: ANDROID_VERSION_CODE=$${ANDROID_VERSION_CODE}")
}
contains (ANDROID_ABIS, x86) {
ANDROID_VERSION_CODE = $$num_add($$ANDROID_VERSION_CODE, 2)
message("Setting for x86: ANDROID_VERSION_CODE=$${ANDROID_VERSION_CODE}")
}
contains (ANDROID_ABIS, x86_64) {
ANDROID_VERSION_CODE = $$num_add($$ANDROID_VERSION_CODE, 3)
message("Setting for x86_64: ANDROID_VERSION_CODE=$${ANDROID_VERSION_CODE}")
}

message("Setting ANDROID_VERSION_NAME=$${ANDROID_VERSION_NAME} ANDROID_VERSION_CODE=$${ANDROID_VERSION_CODE}")

# liboboe requires C++17 for std::timed_mutex
CONFIG += c++17

QT += androidextras
# For device recording permissions
QT += core-private # for Qt6

# enabled only for debugging on android devices
DEFINES += ANDROIDDEBUG
#DEFINES += ANDROIDDEBUG

target.path = /tmp/your_executable # path on device
INSTALLS += target
Expand Down
Loading

0 comments on commit 17b339a

Please sign in to comment.