mirror of
https://github.com/astromaddie/lambda1-vr.git
synced 2026-10-23 07:00:12 +02:00
The Frame runs Android apps in a container (Lepton) that has no OpenXR loader broker, so the APK needs its own loader. This adds a "frame" build type: ./gradlew assembleFrame. It's release plus: - applicationId .frame, minSdk 30 (the container is Android 11) - a manifest overlay that drops the headtracking requirement and the Quest/Pico metadata, since the install fails with it - the Khronos loader 1.1.63, fetched from Maven Central at build time, checked against a SHA-256 and unpacked into build/ (not committed) - L1VR_STEAM_FRAME for the native code, BuildConfig.STEAM_FRAME for Java Quest builds don't see any of this. I also added the gradle wrapper script to Projects/Android, since the wrapper jar and properties were already there, and -Wall for the VR sources. Haven't run it on the headset yet.
226 lines
7.4 KiB
Groovy
226 lines
7.4 KiB
Groovy
buildscript {
|
|
repositories {
|
|
google() // For Gradle 4.0+
|
|
mavenCentral()
|
|
}
|
|
|
|
dependencies {
|
|
classpath 'com.android.tools.build:gradle:8.2.1'
|
|
}
|
|
}
|
|
|
|
apply plugin: 'com.android.application'
|
|
|
|
// Steam Frame build (./gradlew assembleFrame, see docs/STEAM_FRAME_VR.md).
|
|
// The Frame's Android container has no OpenXR loader broker, so the Khronos
|
|
// loader goes into the APK. It comes from Maven Central, gets checked against
|
|
// this hash and is unpacked into build/ (never committed). Quest builds don't
|
|
// touch any of this.
|
|
def khronosLoaderVersion = '1.1.63'
|
|
def khronosLoaderSha256 = '622419d2f6741c3443a3beb4779af0764318edd01830de967f24c741ebcded73'
|
|
def khronosLoaderDir = file("build/openxr-loader/${khronosLoaderVersion}")
|
|
def khronosLoaderSo = new File(khronosLoaderDir, 'libopenxr_loader.so')
|
|
|
|
def fetchKhronosLoader = tasks.register('fetchKhronosLoader') {
|
|
outputs.file khronosLoaderSo
|
|
doLast {
|
|
def url = "https://repo1.maven.org/maven2/org/khronos/openxr/openxr_loader_for_android/${khronosLoaderVersion}/openxr_loader_for_android-${khronosLoaderVersion}.aar"
|
|
def aar = new File(khronosLoaderDir, 'openxr_loader_for_android.aar')
|
|
khronosLoaderDir.mkdirs()
|
|
new URL(url).withInputStream { input -> aar.withOutputStream { it << input } }
|
|
def digest = java.security.MessageDigest.getInstance('SHA-256')
|
|
aar.eachByte(8192) { buf, len -> digest.update(buf, 0, len) }
|
|
def sha = digest.digest().collect { String.format('%02x', it) }.join()
|
|
if (sha != khronosLoaderSha256) {
|
|
aar.delete()
|
|
throw new GradleException("OpenXR loader AAR has the wrong SHA-256 (${sha}), expected ${khronosLoaderSha256}")
|
|
}
|
|
def zip = new java.util.zip.ZipFile(aar)
|
|
try {
|
|
def entry = zip.getEntry('jni/arm64-v8a/libopenxr_loader.so')
|
|
if (entry == null) throw new GradleException('libopenxr_loader.so (arm64-v8a) is not in the loader AAR')
|
|
zip.getInputStream(entry).withStream { input -> khronosLoaderSo.withOutputStream { it << input } }
|
|
} finally {
|
|
zip.close()
|
|
}
|
|
aar.delete()
|
|
}
|
|
}
|
|
|
|
android {
|
|
|
|
namespace = "com.drbeef.lambda1vr"
|
|
|
|
// This is the name of the generated apk file, which will have
|
|
// -debug.apk or -release.apk appended to it.
|
|
// The filename doesn't effect the Android installation process.
|
|
// Use only letters to remain compatible with the package name.
|
|
project.archivesBaseName = "lambda1vr"
|
|
|
|
defaultConfig {
|
|
// Gradle replaces the manifest package with this value, which must
|
|
// be unique on a system. If you don't change it, a new app
|
|
// will replace an older one.
|
|
applicationId "com.drbeef." + project.archivesBaseName
|
|
|
|
externalNativeBuild {
|
|
ndk {
|
|
abiFilters 'arm64-v8a'
|
|
}
|
|
ndkBuild {
|
|
def numProcs = Runtime.runtime.availableProcessors()
|
|
arguments "V=0", "-j$numProcs", "-C$project.buildDir.parent", "APP_PLATFORM=android-32", "NDK_TOOLCHAIN_VERSION=clang", "APP_STL=c++_static"
|
|
abiFilters 'arm64-v8a'
|
|
}
|
|
}
|
|
|
|
minSdkVersion 32
|
|
targetSdkVersion 32
|
|
|
|
// True only in the frame build type
|
|
buildConfigField 'boolean', 'STEAM_FRAME', 'false'
|
|
}
|
|
|
|
buildFeatures {
|
|
buildConfig true
|
|
}
|
|
|
|
externalNativeBuild {
|
|
ndkBuild {
|
|
path file('jni/Android.mk')
|
|
}
|
|
}
|
|
|
|
signingConfigs {
|
|
def keystorePath = (project.hasProperty('key.store')) ?
|
|
new File(project.getProperty('key.store')) :
|
|
project.file('android.debug.keystore')
|
|
|
|
def keystorePassword = (project.hasProperty('key.store.password')) ?
|
|
project.getProperty('key.store.password') : 'android'
|
|
|
|
def keystoreKeyAlias = (project.hasProperty('key.alias')) ?
|
|
project.getProperty('key.alias') : 'androiddebugkey'
|
|
|
|
def keystoreKeyPassword = (project.hasProperty('key.alias.password')) ?
|
|
project.getProperty('key.alias.password') : 'android'
|
|
|
|
debug {
|
|
storeFile keystorePath
|
|
storePassword keystorePassword
|
|
keyAlias keystoreKeyAlias
|
|
keyPassword keystoreKeyPassword
|
|
v2SigningEnabled true
|
|
}
|
|
|
|
release {
|
|
storeFile keystorePath
|
|
storePassword keystorePassword
|
|
keyAlias keystoreKeyAlias
|
|
keyPassword keystoreKeyPassword
|
|
v2SigningEnabled true
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
debug {
|
|
signingConfig signingConfigs.debug
|
|
debuggable true
|
|
jniDebuggable true
|
|
|
|
externalNativeBuild {
|
|
ndkBuild {
|
|
arguments "NDK_DEBUG=1","USE_ASAN=1"
|
|
}
|
|
}
|
|
}
|
|
|
|
release {
|
|
signingConfig signingConfigs.release
|
|
debuggable false
|
|
jniDebuggable false
|
|
|
|
externalNativeBuild {
|
|
ndkBuild {
|
|
arguments "NDK_DEBUG=0","USE_ASAN=0"
|
|
}
|
|
}
|
|
}
|
|
|
|
// Valve Steam Frame (Lepton, Android 11). Same as release, plus the
|
|
// Khronos OpenXR loader, a manifest overlay (src/frame) and L1VR_STEAM_FRAME
|
|
// for the native code. Signed with the debug key, it's only sideloaded.
|
|
frame {
|
|
initWith release
|
|
matchingFallbacks = ['release']
|
|
applicationIdSuffix ".frame"
|
|
versionNameSuffix "-frame"
|
|
signingConfig signingConfigs.debug
|
|
buildConfigField 'boolean', 'STEAM_FRAME', 'true'
|
|
|
|
externalNativeBuild {
|
|
ndkBuild {
|
|
arguments "L1VR_STEAM_FRAME=1", "L1VR_OPENXR_LOADER=${khronosLoaderSo.absolutePath}", "APP_PLATFORM=android-30"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sourceSets.main {
|
|
manifest.srcFile 'AndroidManifest.xml'
|
|
java.srcDirs = ['../../java']
|
|
jniLibs.srcDir 'libs'
|
|
res.srcDirs = ['../../res']
|
|
assets.srcDirs = ['../../assets']
|
|
jni.srcDirs = ['jni']
|
|
}
|
|
|
|
lintOptions {
|
|
checkReleaseBuilds false
|
|
disable 'ExpiredTargetSdkVersion'
|
|
}
|
|
|
|
//packagingOptions {
|
|
// exclude 'lib/arm64-v8a/libopenxr_loader.so'
|
|
//}
|
|
|
|
compileOptions {
|
|
sourceCompatibility = '1.8'
|
|
targetCompatibility = '1.8'
|
|
}
|
|
compileSdkVersion = 32
|
|
buildToolsVersion = '29.0.3'
|
|
|
|
ndkVersion '25.1.8937393'
|
|
}
|
|
|
|
// Lepton runs Android 11, the Quest build needs 12L (API 32)
|
|
androidComponents {
|
|
beforeVariants(selector().withBuildType('frame')) { variantBuilder ->
|
|
variantBuilder.minSdk = 30
|
|
}
|
|
}
|
|
|
|
// The loader has to be there before ndk-build links against it
|
|
tasks.configureEach { task ->
|
|
if (task.name == 'preFrameBuild') {
|
|
task.dependsOn fetchKhronosLoader
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
implementation "com.android.support:support-compat:28.0.0"
|
|
implementation "com.android.support:support-core-utils:28.0.0"
|
|
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
|
}
|
|
|
|
repositories {
|
|
google()
|
|
}
|
|
|
|
// Workaround to fix issue in Android Studio Chipmunk 2021.2.1 and later
|
|
// where opening a project would result in a 'prepareKotlinBuildScriptModel'
|
|
// not found error
|
|
if (!tasks.findByName("prepareKotlinBuildScriptModel")) {
|
|
tasks.register("prepareKotlinBuildScriptModel") {}
|
|
} |