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,38 +160,58 @@ 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.
163+ private fun setupProject (projectPath : String , gradleBuildDir : String , outputHandler : (Int , String ) -> Unit ): File {
164+ val projectTreeUri: Uri
165+ val workDir = Utils .getProjectCacheDir(context, projectPath, gradleBuildDir)
162166 if (workDir.exists()) {
163- val apkAssetsDir = File (workDir, " src/main/assets" )
164- if (apkAssetsDir.exists()) {
165- apkAssetsDir.deleteRecursively()
167+ val info = ProjectInfo .readFromDirectory(workDir)
168+ if (info != null ) {
169+ projectTreeUri = info.projectTreeUri.toUri()
170+ } else {
171+ throw Exception (" Could not get projectTreeUri." )
166172 }
167-
168- val aabAssetsDir = File (workDir, " assetPackInstallTime/src/main/assets" )
169- if (aabAssetsDir.exists()) {
170- aabAssetsDir.deleteRecursively()
173+ } else {
174+ if (checkSelfPermission(context, Manifest .permission.POST_NOTIFICATIONS ) == PackageManager .PERMISSION_GRANTED ) {
175+ outputHandler(OUTPUT_STDERR , " Project path \" $projectPath \" is not accessible. Click on notification and give GABE app access to this directory." )
176+ Utils .showDirectoryAccessNotification(context, projectPath)
177+ } else {
178+ throw SecurityException (" POST_NOTIFICATIONS permission not granted. Please grant POST_NOTIFICATIONS permission for GABE app and retry." )
171179 }
172- }
173180
174- if (! FileUtils .tryCopyDirectory(fullPath, workDir)) {
175- throw IOException (" Failed to copy $fullPath to $workDir " )
181+ // 2 minute wait should be ideal?
182+ projectTreeUri = waitForDirectoryAccess(2 * 60 * 1000 )
183+ ? : throw Exception (" Directory access not granted in time. Build canceled." )
184+ outputHandler(OUTPUT_STDOUT , " Access granted for $projectPath . Starting Gradle build..." )
185+ workDir.mkdirs()
186+ ProjectInfo .writeToDirectory(context, workDir, projectPath, gradleBuildDir, projectTreeUri)
176187 }
177188
178- ProjectInfo .writeToDirectory(workDir, projectPath, gradleBuildDir )
179-
189+ outputHandler( OUTPUT_INFO , " > Importing project files... " )
190+ FileUtils .importAndroidProject(context, projectTreeUri, gradleBuildDir, workDir)
180191 return workDir
181192 }
182193
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)
194+ private fun fixGradleArgs (projectPath : String , rawGradleArgs : List <String >): List <String > {
195+ val normalizedProjectPath = projectPath.trimEnd(' /' )
196+ return rawGradleArgs.map { arg ->
197+ when {
198+ arg.startsWith(" -Pdebug_keystore_file=" ) -> " -Pdebug_keystore_file=/project/.android/debug.keystore"
199+ arg.startsWith(" -Prelease_keystore_file=" ) -> " -Prelease_keystore_file=/project/.android/release.keystore"
200+ arg.startsWith(" -Paddons_directory=" ) -> " -Paddons_directory=/project/addons"
201+
202+ arg.startsWith(" -Pplugins_local_binaries=" ) -> {
203+ val prefix = " -Pplugins_local_binaries="
204+ val value = arg.removePrefix(prefix)
205+ val updated = value.replace(" $normalizedProjectPath /addons" , " /project/addons" )
206+ prefix + updated
207+ }
208+ else -> arg
209+ }
210+ }
211+ }
187212
213+ fun cleanProject (projectPath : String , gradleBuildDir : String ) {
214+ val workDir = Utils .getProjectCacheDir(context, projectPath, gradleBuildDir)
188215 if (workDir.exists()) {
189216 workDir.deleteRecursively()
190217 }
@@ -336,7 +363,7 @@ class BuildEnvironment(
336363 gradleCmd,
337364 )
338365 val binds = listOf (
339- Environment .getExternalStorageDirectory().absolutePath ,
366+ " /storage/emulated/0:/storage/emulated/0 " ,
340367 " ${workDir.absolutePath} :/project" ,
341368 " ${gradleCache.absolutePath} :/project/?" ,
342369 )
@@ -348,14 +375,14 @@ class BuildEnvironment(
348375 return AppPaths .getRootfsReadyFile(File (rootfs)).exists()
349376 }
350377
351- fun executeGradle (gradleArgs : List <String >, projectPath : String , gradleBuildDir : String , outputHandler : (Int , String ) -> Unit ): Int {
378+ fun executeGradle (rawGradleArgs : List <String >, projectPath : String , gradleBuildDir : String , outputHandler : (Int , String ) -> Unit ): Int {
352379 if (! isRootfsReady()) {
353380 outputHandler(OUTPUT_STDERR , " Rootfs isn't installed. Install it in the Godot Gradle Build Environment app." )
354381 return 255
355382 }
356383
357384 val workDir = try {
358- setupProject(projectPath, gradleBuildDir)
385+ setupProject(projectPath, gradleBuildDir, outputHandler )
359386 } catch (e: Exception ) {
360387 outputHandler(OUTPUT_STDERR , " Unable to setup project: ${e.message} " )
361388 return 255
@@ -371,12 +398,14 @@ class BuildEnvironment(
371398 outputHandler(type, line)
372399 }
373400
401+ val gradleArgs = fixGradleArgs(projectPath, rawGradleArgs)
402+
374403 var result = executeGradleInternal(gradleArgs, workDir, captureOutputHandler)
375404
376405 val stderr = stderrBuilder.toString()
377406 if (result == 0 && stderr.contains(" BUILD FAILED" )) {
378407 // Sometimes Gradle builds fail, but it still gives an exit code of 0.
379- result = 1 ;
408+ result = 1
380409 }
381410 stderrBuilder.clear()
382411
@@ -399,7 +428,7 @@ class BuildEnvironment(
399428 result = executeGradleInternal(gradleArgs, workDir, captureOutputHandler)
400429 val stderr = stderrBuilder.toString()
401430 if (result == 0 && stderr.contains(" BUILD FAILED" )) {
402- result = 1 ;
431+ result = 1
403432 }
404433 }
405434
@@ -417,4 +446,25 @@ class BuildEnvironment(
417446 }
418447 }
419448
420- }
449+ fun waitForDirectoryAccess (timeoutMs : Long ): Uri ? {
450+ val endTime = System .currentTimeMillis() + timeoutMs
451+
452+ synchronized(accessLock) {
453+ while (grantedTreeUri == null ) {
454+ val remaining = endTime - System .currentTimeMillis()
455+ if (remaining <= 0 ) {
456+ return null
457+ }
458+ accessLock.wait(remaining)
459+ }
460+ return grantedTreeUri
461+ }
462+ }
463+
464+ fun onDirectoryAccessGranted (uri : Uri ) {
465+ synchronized(accessLock) {
466+ grantedTreeUri = uri
467+ accessLock.notifyAll()
468+ }
469+ }
470+ }
0 commit comments