From eb0c8951196c637e44daf3c0617b131b997d5d2c Mon Sep 17 00:00:00 2001 From: Lena Date: Thu, 1 Jan 2026 00:00:00 +0000 Subject: scrcpy-android: mirror an Android device over wireless ADB Native Java app for Android 12+ that mirrors another Android device over wireless ADB, forwarding video, audio, touch input, and clipboard. Bundles a pinned scrcpy-server.jar and the vendored libadb-android stack. Supports h264/h265/av1 video and raw/opus audio with in-app codec selection. No NDK, no Kotlin. Includes JVM unit tests and a Docker-based emulator e2e rig. --- scripts/build-apk | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100755 scripts/build-apk (limited to 'scripts/build-apk') diff --git a/scripts/build-apk b/scripts/build-apk new file mode 100755 index 0000000..9fd1409 --- /dev/null +++ b/scripts/build-apk @@ -0,0 +1,83 @@ +#!/bin/sh +# Build a signed release APK. +# +# Required environment: +# KEYSTORE_PATH absolute path to a JKS / PKCS12 keystore +# KEYSTORE_PASS password for the keystore +# KEY_ALIAS alias of the signing key inside the keystore +# KEY_PASS password for that key (often the same as KEYSTORE_PASS) +# +# Optional: +# ANDROID_SDK_ROOT if set, apksigner is located via this; otherwise +# the script trusts gradle's output and skips the +# post-build verification step. +# +# Usage: +# scripts/build-apk +# +# Output: prints the absolute path of the signed APK and its sha256. +# The signed APK lives at app/build/outputs/apk/release/app-release.apk. +# +# This script does NOT generate a keystore. Generate one once with: +# keytool -genkey -v -keystore release.jks -alias scrcpy-android \ +# -keyalg RSA -keysize 2048 -validity 10000 +# and keep it outside the repo (it must never be committed). + +set -eu + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +APK="$ROOT/app/build/outputs/apk/release/app-release.apk" +JAR="$ROOT/app/src/main/assets/scrcpy-server.jar" + +require() { + name="$1" + eval "val=\${$name:-}" + if [ -z "$val" ]; then + echo "build-apk: \$$name is not set" >&2 + echo " see the header of $0 for the required environment" >&2 + exit 1 + fi +} + +require KEYSTORE_PATH +require KEYSTORE_PASS +require KEY_ALIAS +require KEY_PASS + +if [ ! -f "$KEYSTORE_PATH" ]; then + echo "build-apk: keystore not found at $KEYSTORE_PATH" >&2 + exit 1 +fi + +if [ ! -f "$JAR" ]; then + echo "build-apk: $JAR is missing" >&2 + echo " run scripts/update-server first" >&2 + exit 1 +fi + +echo "build-apk: gradle :app:assembleRelease" +cd "$ROOT" +./gradlew --no-daemon :app:assembleRelease + +if [ ! -f "$APK" ]; then + echo "build-apk: gradle finished but $APK does not exist" >&2 + exit 1 +fi + +SUM=$(sha256sum "$APK" | awk '{print $1}') + +# Best-effort: if apksigner is reachable, confirm the signature. +APKSIGNER="" +if [ -n "${ANDROID_SDK_ROOT:-}" ]; then + APKSIGNER=$(ls "$ANDROID_SDK_ROOT"/build-tools/*/apksigner 2>/dev/null | sort | tail -n 1 || true) +fi +if [ -z "$APKSIGNER" ] && command -v apksigner >/dev/null 2>&1; then + APKSIGNER=$(command -v apksigner) +fi +if [ -n "$APKSIGNER" ]; then + echo "build-apk: apksigner verify" + "$APKSIGNER" verify --verbose "$APK" | sed 's/^/ /' +fi + +echo "build-apk: $APK" +echo "build-apk: sha256 $SUM" -- cgit v1.2.3