11package org.godotengine.godot_gradle_build_environment
22
3+ import android.Manifest
34import android.content.Context
5+ import android.content.pm.PackageManager
6+ import android.net.Uri
47import android.os.Build
5- import android.os.Environment
68import android.util.Log
9+ import androidx.core.content.ContextCompat.checkSelfPermission
10+ import androidx.core.net.toUri
711import java.io.BufferedReader
812import java.io.File
913import java.io.FileOutputStream
@@ -36,6 +40,9 @@ class BuildEnvironment(
3640
3741 private var currentProcess: Process ? = null
3842
43+ private val accessLock = Object ()
44+ @Volatile private var grantedTreeUri: Uri ? = null
45+
3946 private fun getDefaultEnv (): List <String > {
4047 return try {
4148 File (rootfs, " env" ).readLines()
@@ -153,41 +160,59 @@ class BuildEnvironment(
153160 return exitCode
154161 }
155162
156- private fun setupProject (projectPath : String , gradleBuildDir : String ): File {
157- val fullPath = File (projectPath, gradleBuildDir)
158- val hash = Integer .toHexString(fullPath.absolutePath.hashCode())
159- val workDir = File (projectRoot, hash)
160-
161- // Clean up assets from a previous export.
162- if (workDir.exists()) {
163- val apkAssetsDir = File (workDir, " src/main/assets" )
164- if (apkAssetsDir.exists()) {
165- apkAssetsDir.deleteRecursively()
163+ private fun setupProject (projectPath : String , gradleBuildDir : String , outputHandler : (Int , String ) -> Unit ): File {
164+ var projectTreeUri = FileUtils .getProjectTreeUri(context, projectPath)
165+ if (projectTreeUri == null ) {
166+ if (checkSelfPermission(context, Manifest .permission.POST_NOTIFICATIONS ) == PackageManager .PERMISSION_GRANTED ) {
167+ outputHandler(OUTPUT_STDERR , " Project path \" $projectPath \" is not accessible. Click on notification and give GABE app access to this directory." )
168+ Utils .showDirectoryAccessNotification(context, projectPath)
169+ } else {
170+ throw SecurityException (" POST_NOTIFICATIONS permission not granted. Please grant POST_NOTIFICATIONS permission for GABE app and retry." )
166171 }
167172
168- val aabAssetsDir = File (workDir, " assetPackInstallTime/src/main/assets" )
169- if (aabAssetsDir.exists()) {
170- aabAssetsDir.deleteRecursively()
171- }
173+ // 2 minute wait should be ideal?
174+ projectTreeUri = waitForDirectoryAccess(2 * 60 * 1000 )
175+ ? : throw Exception (" Directory access not granted in time. Build canceled." )
176+ outputHandler(OUTPUT_INFO , " Access granted for $projectPath . Starting Gradle build..." )
177+ FileUtils .saveProjectTreeUri(context, projectPath, projectTreeUri)
172178 }
173179
174- if (! FileUtils .tryCopyDirectory(fullPath, workDir)) {
175- throw IOException (" Failed to copy $fullPath to $workDir " )
180+ val workDir = Utils .getProjectCacheDir(context, projectPath, gradleBuildDir)
181+ if (! workDir.exists()) {
182+ workDir.mkdirs()
183+ ProjectInfo .writeToDirectory(context, workDir, projectPath, gradleBuildDir, projectTreeUri)
176184 }
177185
178- ProjectInfo .writeToDirectory(workDir, projectPath, gradleBuildDir )
179-
186+ outputHandler( OUTPUT_INFO , " > Importing project files... " )
187+ FileUtils .importAndroidProject(context, projectTreeUri, gradleBuildDir, workDir)
180188 return workDir
181189 }
182190
183- fun cleanProject (projectPath : String , gradleBuildDir : String ) {
184- val fullPath = File (projectPath, gradleBuildDir)
185- val hash = Integer .toHexString(fullPath.absolutePath.hashCode())
186- val workDir = File (projectRoot, hash)
191+ private fun fixGradleArgs (projectPath : String , rawGradleArgs : List <String >): List <String > {
192+ val normalizedProjectPath = projectPath.trimEnd(' /' )
193+ return rawGradleArgs.map { arg ->
194+ when {
195+ arg.startsWith(" -Pdebug_keystore_file=" ) -> " -Pdebug_keystore_file=/project/.android/debug.keystore"
196+ arg.startsWith(" -Prelease_keystore_file=" ) -> " -Prelease_keystore_file=/project/.android/release.keystore"
197+ arg.startsWith(" -Paddons_directory=" ) -> " -Paddons_directory=/project/addons"
198+
199+ arg.startsWith(" -Pplugins_local_binaries=" ) -> {
200+ val prefix = " -Pplugins_local_binaries="
201+ val value = arg.removePrefix(prefix)
202+ val updated = value.replace(" $normalizedProjectPath /addons" , " /project/addons" )
203+ prefix + updated
204+ }
205+ else -> arg
206+ }
207+ }
208+ }
187209
210+ fun cleanProject (projectPath : String , gradleBuildDir : String ) {
211+ val workDir = Utils .getProjectCacheDir(context, projectPath, gradleBuildDir)
188212 if (workDir.exists()) {
189213 workDir.deleteRecursively()
190214 }
215+ FileUtils .deleteProjectTreeUri(context, projectPath)
191216 }
192217
193218 fun cleanGlobalCache () {
@@ -336,7 +361,7 @@ class BuildEnvironment(
336361 gradleCmd,
337362 )
338363 val binds = listOf (
339- Environment .getExternalStorageDirectory().absolutePath ,
364+ " /storage/emulated/0:/storage/emulated/0 " ,
340365 " ${workDir.absolutePath} :/project" ,
341366 " ${gradleCache.absolutePath} :/project/?" ,
342367 )
@@ -348,14 +373,14 @@ class BuildEnvironment(
348373 return AppPaths .getRootfsReadyFile(File (rootfs)).exists()
349374 }
350375
351- fun executeGradle (gradleArgs : List <String >, projectPath : String , gradleBuildDir : String , outputHandler : (Int , String ) -> Unit ): Int {
376+ fun executeGradle (rawGradleArgs : List <String >, projectPath : String , gradleBuildDir : String , outputHandler : (Int , String ) -> Unit ): Int {
352377 if (! isRootfsReady()) {
353378 outputHandler(OUTPUT_STDERR , " Rootfs isn't installed. Install it in the Godot Gradle Build Environment app." )
354379 return 255
355380 }
356381
357382 val workDir = try {
358- setupProject(projectPath, gradleBuildDir)
383+ setupProject(projectPath, gradleBuildDir, outputHandler )
359384 } catch (e: Exception ) {
360385 outputHandler(OUTPUT_STDERR , " Unable to setup project: ${e.message} " )
361386 return 255
@@ -371,12 +396,14 @@ class BuildEnvironment(
371396 outputHandler(type, line)
372397 }
373398
399+ val gradleArgs = fixGradleArgs(projectPath, rawGradleArgs)
400+
374401 var result = executeGradleInternal(gradleArgs, workDir, captureOutputHandler)
375402
376403 val stderr = stderrBuilder.toString()
377404 if (result == 0 && stderr.contains(" BUILD FAILED" )) {
378405 // Sometimes Gradle builds fail, but it still gives an exit code of 0.
379- result = 1 ;
406+ result = 1
380407 }
381408 stderrBuilder.clear()
382409
@@ -399,7 +426,7 @@ class BuildEnvironment(
399426 result = executeGradleInternal(gradleArgs, workDir, captureOutputHandler)
400427 val stderr = stderrBuilder.toString()
401428 if (result == 0 && stderr.contains(" BUILD FAILED" )) {
402- result = 1 ;
429+ result = 1
403430 }
404431 }
405432
@@ -417,4 +444,25 @@ class BuildEnvironment(
417444 }
418445 }
419446
420- }
447+ fun waitForDirectoryAccess (timeoutMs : Long ): Uri ? {
448+ val endTime = System .currentTimeMillis() + timeoutMs
449+
450+ synchronized(accessLock) {
451+ while (grantedTreeUri == null ) {
452+ val remaining = endTime - System .currentTimeMillis()
453+ if (remaining <= 0 ) {
454+ return null
455+ }
456+ accessLock.wait(remaining)
457+ }
458+ return grantedTreeUri
459+ }
460+ }
461+
462+ fun onDirectoryAccessGranted (uri : Uri ) {
463+ synchronized(accessLock) {
464+ grantedTreeUri = uri
465+ accessLock.notifyAll()
466+ }
467+ }
468+ }
0 commit comments