3be310ade6
build / build (push) Successful in 1m52s
Expand the on-screen keyboard to a full US layout (letters, digits, punctuation, Shift layer, Tab, arrows, F1-F12, one-shot Ctrl/Alt modifiers), make it a persistent, movable, closeable panel via a title-bar drag handle, and lower its opacity. Previously the keyboard was just a second panel swapped into the same quad as the menu launcher, which made the launcher translucent too and made the keyboard and menu mutually exclusive. Extract the shared swapchain/JNI/touch-dispatch plumbing into two generic, reusable modules - xr_swapchain.c (swapchain + per-image FBO setup) and xr_overlay.c (an off-screen Android View rendered into its own OpenXR quad, hit-tested via a laser pointer) - and make the menu launcher and keyboard two independent XrOverlay instances instead of one. The controller's menu button now only opens/closes the launcher; the launcher's "Keyboard" button only opens the keyboard - so the keyboard can stay up while picking something from the menu, and only the keyboard's own Close button hides it. xr_menu.c/.h are gone, fully absorbed into xr_overlay.c. On the Java side, OverlayPanel.java carries the shared off-screen-render/touch/ cursor plumbing that MenuOverlay and the new KeyboardOverlay both subclass.
204 lines
11 KiB
Groovy
204 lines
11 KiB
Groovy
apply plugin: 'com.android.application'
|
|
|
|
// Paths written by build-image/prepare-android-project.sh (called by
|
|
// build-apk.sh, or standalone with --host-paths to prep for a native
|
|
// build in Android Studio - see README) since they're only known at
|
|
// prep/build time, not something a checked-in build.gradle can hardcode.
|
|
// engineDir is the scratch, patched copy of engine/; gl4esDir is the
|
|
// scratch, patched copy of android/gl4es-src/ (built from source as a
|
|
// CMake subdirectory of engine/'s own native build below, not prebuilt);
|
|
// prebuiltDir is the Android SDL2/SDL2_mixer/fluidsynth-lite/openxr
|
|
// prebuilts - all point into this container's own paths for
|
|
// build-apk.sh's own gradlew call, or host-resolvable paths (plus a
|
|
// host-side export of prebuiltDir, since /opt/prebuilt/android only
|
|
// exists in the image) when prepared with --host-paths for Android
|
|
// Studio.
|
|
def engineProps = new Properties()
|
|
file("${projectDir}/../engine.properties").withInputStream { engineProps.load(it) }
|
|
def engineDir = engineProps.getProperty('engineDir')
|
|
if (engineDir == null) {
|
|
throw new GradleException("android/engine.properties is missing 'engineDir' - run via build-apk.sh or prepare-android-project.sh, not gradlew directly")
|
|
}
|
|
def gl4esDir = engineProps.getProperty('gl4esDir')
|
|
if (gl4esDir == null) {
|
|
throw new GradleException("android/engine.properties is missing 'gl4esDir' - run via build-apk.sh or prepare-android-project.sh, not gradlew directly")
|
|
}
|
|
def prebuiltDir = engineProps.getProperty('prebuiltDir')
|
|
if (prebuiltDir == null) {
|
|
throw new GradleException("android/engine.properties is missing 'prebuiltDir' - run via build-apk.sh or prepare-android-project.sh, not gradlew directly")
|
|
}
|
|
|
|
// Set by build-apk.sh (see build-image/version.sh, shared with the
|
|
// desktop tarball's versioning) via -PquestshockVersionName/Code. Default
|
|
// here only covers a direct, unsupported `gradlew` invocation.
|
|
def questshockVersionName = project.hasProperty('questshockVersionName') ? project.property('questshockVersionName') : '0.0.0-dev'
|
|
def questshockVersionCode = project.hasProperty('questshockVersionCode') ? project.property('questshockVersionCode').toInteger() : 1
|
|
|
|
// Auto-refreshes the staged, patched engine/ and gl4es-src/ copies and
|
|
// prebuilt libraries (see build-image/prepare-android-project.sh
|
|
// --host-paths, and the engineDir/gl4esDir/prebuiltDir properties read
|
|
// above) so a plain Android Studio build/run can never silently compile
|
|
// against a stale scratch copy after engine/, android/engine-patches/,
|
|
// android/gl4es-src/, or android/gl4es-patches/ change - previously a
|
|
// manual, easy to forget re-run. inputs/outputs are declared so Gradle
|
|
// skips the (Docker-invoking, not free) step entirely when nothing
|
|
// relevant actually changed, keeping pure-Java edit/run cycles fast.
|
|
//
|
|
// This step only re-stages whatever is already baked into the build-image
|
|
// (it re-runs prepare-android-project.sh inside the existing image, which
|
|
// is fast - no compilation happens here) - engine/ and gl4es-src/ are both
|
|
// then compiled from these scratch copies by the externalNativeBuild CMake
|
|
// configure below (see android/engine-patches/02-android-opengl-es.patch's
|
|
// add_subdirectory of gl4esDir), so Gradle's own incremental CMake/Ninja
|
|
// build only recompiles what actually changed, same as any other native
|
|
// source edit. This task does NOT rebuild the image itself: if you changed
|
|
// build-image/Dockerfile (a real toolchain/dependency change, not
|
|
// engine-patches/ or gl4es-patches/, which this task re-stages on its own),
|
|
// you still need to run ./build-image.sh yourself first (a slow, deliberate
|
|
// step - see the memory on build-image versioning) so the image actually
|
|
// contains your changes before this task re-stages them.
|
|
def repoRoot = file("${projectDir}/../..")
|
|
def stageEngineTask = tasks.register("stageEngine", Exec) {
|
|
group = "build setup"
|
|
description = "Refreshes the patched engine/ scratch copy and prebuilt libraries for a native Android Studio build (build-image/prepare-android-project.sh --host-paths)."
|
|
workingDir repoRoot
|
|
commandLine "./run-image.sh", "bash", "build-image/prepare-android-project.sh", "--host-paths"
|
|
|
|
// /opt/prebuilt/android only ever exists inside the build-image
|
|
// container itself (baked in at image-build time, never on the host -
|
|
// see build-image/Dockerfile). Its presence means this build is
|
|
// build-apk.sh's own gradlew call, running INSIDE that container, which
|
|
// already staged everything itself in container mode before invoking
|
|
// gradlew - re-running this task there would try to `docker run` from
|
|
// inside a container with no docker socket, breaking make apk/CI. Only
|
|
// a genuine host-side Android Studio build (where this path is absent)
|
|
// needs this task.
|
|
onlyIf { !file('/opt/prebuilt/android').isDirectory() }
|
|
|
|
inputs.dir("${repoRoot}/engine")
|
|
inputs.dir("${projectDir}/../engine-patches")
|
|
inputs.dir("${projectDir}/../gl4es-src")
|
|
inputs.dir("${projectDir}/../gl4es-patches")
|
|
inputs.file("${repoRoot}/build-image/Dockerfile")
|
|
outputs.dir("${repoRoot}/build/android-engine")
|
|
outputs.dir("${repoRoot}/build/android-gl4es")
|
|
outputs.dir("${repoRoot}/build/android-prebuilt")
|
|
outputs.file("${projectDir}/../engine.properties")
|
|
}
|
|
|
|
// preBuild is what every variant's compile/native-build tasks already
|
|
// transitively depend on, regardless of AGP version's exact CMake task
|
|
// naming - the simplest reliable hook to run before any of them.
|
|
afterEvaluate {
|
|
tasks.named("preBuild").configure {
|
|
dependsOn stageEngineTask
|
|
}
|
|
}
|
|
|
|
android {
|
|
namespace "de.ladkau.questshock"
|
|
// compileSdk/buildToolsVersion/ndkVersion must all match what
|
|
// build-image/Dockerfile actually installs - otherwise AGP defaults to
|
|
// whatever version it itself prefers and tries to download it into the
|
|
// SDK dir at build time, which isn't writable.
|
|
compileSdk 34
|
|
buildToolsVersion "34.0.0"
|
|
ndkVersion "26.1.10909125"
|
|
|
|
defaultConfig {
|
|
applicationId "de.ladkau.questshock"
|
|
// 29, not compileSdk's 34, is deliberate - see build-image/Dockerfile's
|
|
// comment on ANDROID_PLATFORM_VERSION: paired with
|
|
// requestLegacyExternalStorage in the manifest, this keeps
|
|
// /sdcard/questshock/ a plain, unrestricted shared folder instead of
|
|
// Android 11+ scoped storage, on the Quest 2/3/3S/Pro headsets this
|
|
// targets - not just whatever's the current API level today.
|
|
minSdkVersion 24
|
|
targetSdkVersion 29
|
|
versionCode questshockVersionCode
|
|
versionName questshockVersionName
|
|
|
|
externalNativeBuild {
|
|
cmake {
|
|
// engine/CMakeLists.txt's own dependency-selection options
|
|
// (see engine/CMakeLists.txt near the top): SDL2 via
|
|
// find_package (our Android SDL2 build installs a real CMake
|
|
// config, unlike the desktop build's older bundled copy);
|
|
// SDL2_mixer and FluidSynth via the same build_ext/ BUNDLED
|
|
// convention the desktop build already uses (populated in the
|
|
// scratch engine copy by prepare-android-project.sh from
|
|
// prebuiltDir). ANDROID_GL4ES_DIR is read by
|
|
// android/engine-patches/02-android-opengl-es.patch, which
|
|
// add_subdirectory()'s it to compile gl4es from source as
|
|
// part of this same CMake configure, instead of linking a
|
|
// prebuilt .so - so gl4es-patches/ changes just need a normal
|
|
// rebuild here, not a build-image rebuild.
|
|
// The CMAKE_FIND_ROOT_PATH* overrides below are needed
|
|
// because the NDK toolchain file restricts find_package/
|
|
// find_path/find_library to its own sysroot by default,
|
|
// which would otherwise miss our custom-installed SDL2 (see
|
|
// build-image/Dockerfile's SDL2_mixer build, which hit the
|
|
// exact same thing).
|
|
// -Wl,-z,max-page-size=16384: NDK 26 doesn't 16 KB-align ELF
|
|
// LOAD segments by default (only automatic in NDK 28+) - see
|
|
// build-image/Dockerfile's ANDROID_16KB_LDFLAGS, which does
|
|
// the same for the prebuilt SDL2/SDL2_mixer/fluidsynth-lite
|
|
// .so's this links against (gl4es is compiled here with the
|
|
// same flag passed straight through, see its add_subdirectory
|
|
// above).
|
|
// ANDROID_EXTRA_SOURCES is a semicolon-separated CMake list,
|
|
// not shell-split - AGP passes each "arguments" string
|
|
// straight through as one argv element to cmake, so the
|
|
// ';' below survives intact. ANDROID_EXTRA_INCLUDE_DIRS lets
|
|
// engine/'s own OpenGL.cc find "xr_session.h" (see
|
|
// android/engine-patches/10-android-openxr-cmake.patch,
|
|
// which adds it to include_directories()).
|
|
// USE_ANDROID_LOG/STATICLIB used to be passed to gl4es's own,
|
|
// separate cmake invocation in build-image/Dockerfile - now
|
|
// that it's add_subdirectory()'d into this same configure,
|
|
// they need to be set here instead, or gl4es silently falls
|
|
// back to stdio logging, which never reaches logcat. ANDROID
|
|
// itself doesn't need setting here - the NDK toolchain file
|
|
// already defines it before any CMakeLists.txt runs.
|
|
arguments "-DENABLE_SDL2=ON", "-DENABLE_SOUND=BUNDLED", "-DENABLE_FLUIDSYNTH=BUNDLED", \
|
|
"-DANDROID_PREBUILT_DIR=${prebuiltDir}", \
|
|
"-DANDROID_GL4ES_DIR=${gl4esDir}", \
|
|
"-DUSE_ANDROID_LOG=ON", "-DSTATICLIB=OFF", \
|
|
"-DANDROID_EXTRA_INCLUDE_DIRS=${projectDir}/src/main/cpp", \
|
|
"-DCMAKE_PREFIX_PATH=${prebuiltDir}/sdl2", \
|
|
"-DCMAKE_FIND_ROOT_PATH=${prebuiltDir}/sdl2", \
|
|
"-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH", \
|
|
"-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH", \
|
|
"-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH", \
|
|
"-DCMAKE_SHARED_LINKER_FLAGS=-Wl,-z,max-page-size=16384", \
|
|
"-DANDROID_EXTRA_SOURCES=${projectDir}/src/main/cpp/questshock_native.c;${projectDir}/src/main/cpp/xr_session.c;${projectDir}/src/main/cpp/xr_input.c;${projectDir}/src/main/cpp/xr_overlay.c;${projectDir}/src/main/cpp/xr_swapchain.c"
|
|
abiFilters 'arm64-v8a'
|
|
}
|
|
}
|
|
}
|
|
|
|
externalNativeBuild {
|
|
cmake {
|
|
path file("${engineDir}/CMakeLists.txt")
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
minifyEnabled false
|
|
}
|
|
}
|
|
|
|
lint {
|
|
abortOnError false
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
// ActivityCompat.checkSelfPermission/requestPermissions in
|
|
// QuestShockActivity - the plain android.app.Activity APIs work the
|
|
// same way without androidx, but ActivityCompat is the standard,
|
|
// documented way to request runtime permissions safely.
|
|
implementation 'androidx.core:core:1.12.0'
|
|
}
|