diff --git a/app/build.gradle b/app/build.gradle
index 15d2728..dd097a8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -59,9 +59,12 @@ android {
}
horizonos {
dimension "android_distribution"
- applicationIdSuffix ".horizonos"
versionNameSuffix "-horizonos"
}
+ picoos {
+ dimension "android_distribution"
+ versionNameSuffix "-picoos"
+ }
}
compileOptions {
diff --git a/app/src/horizonos/AndroidManifest.xml b/app/src/horizonos/AndroidManifest.xml
new file mode 100644
index 0000000..49ef05b
--- /dev/null
+++ b/app/src/horizonos/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 84e3d0f..2db00b2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,12 +2,6 @@
-
-
Unit): File {
+ val workDir = Utils.getProjectCacheDir(context, projectPath, gradleBuildDir)
+
+ if (BuildConfig.FLAVOR == "picoos" || BuildConfig.FLAVOR == "horizonos") {
+ val sourceDir = File(projectPath, gradleBuildDir)
+
+ if (workDir.exists()) {
+ val apkAssetsDir = File(workDir, "src/main/assets")
+ if (apkAssetsDir.exists()) apkAssetsDir.deleteRecursively()
+
+ val aabAssetsDir = File(workDir, "assetPackInstallTime/src/main/assets")
+ if (aabAssetsDir.exists()) aabAssetsDir.deleteRecursively()
+ } else {
+ workDir.mkdirs()
+ }
+
+ outputHandler(OUTPUT_INFO, "> Importing project files...")
+ if (!FileUtils.tryCopyDirectory(sourceDir, workDir)) {
+ throw IOException("Failed to copy $sourceDir to $workDir")
+ }
+
+ ProjectInfo.writeToDirectoryXR(workDir, projectPath, gradleBuildDir)
+ return workDir
+ }
+
var projectTreeUri = FileUtils.getProjectTreeUri(context, projectPath)
if (projectTreeUri == null) {
val notificationPerm = checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS)
@@ -182,7 +206,7 @@ class BuildEnvironment(private val context: Context, private val rootfs: String,
projectTreeUri = waitForDirectoryAccess(DIR_ACCESS_WAIT_DURATION)
?: throw Exception("Directory access not granted in time. Build canceled.")
- outputHandler(OUTPUT_INFO, "Access granted for $projectPath. Starting Gradle build...")
+ outputHandler(OUTPUT_INFO, "Access granted for $projectPath.\nStarting Gradle build...")
FileUtils.saveProjectTreeUri(context, projectPath, projectTreeUri)
// Notify user if limit is reached so they can clear older projects.
@@ -197,7 +221,6 @@ class BuildEnvironment(private val context: Context, private val rootfs: String,
}
}
- val workDir = Utils.getProjectCacheDir(context, projectPath, gradleBuildDir)
if (!workDir.exists()) {
workDir.mkdirs()
ProjectInfo.writeToDirectory(context, workDir, projectPath, gradleBuildDir, projectTreeUri)
@@ -425,7 +448,12 @@ class BuildEnvironment(private val context: Context, private val rootfs: String,
outputHandler(type, line)
}
- val gradleArgs = fixGradleArgs(projectPath, rawGradleArgs)
+ val gradleArgs = if (BuildConfig.FLAVOR == "picoos" || BuildConfig.FLAVOR == "horizonos") {
+ // GABE has full storage access on XR devices, so we are not pulling addons dir, or keystore files.
+ rawGradleArgs
+ } else {
+ fixGradleArgs(projectPath, rawGradleArgs)
+ }
var result = executeGradleInternal(gradleArgs, workDir, captureOutputHandler)
diff --git a/app/src/main/java/org/godotengine/godot_gradle_build_environment/FileUtils.kt b/app/src/main/java/org/godotengine/godot_gradle_build_environment/FileUtils.kt
index 0249093..aae4016 100644
--- a/app/src/main/java/org/godotengine/godot_gradle_build_environment/FileUtils.kt
+++ b/app/src/main/java/org/godotengine/godot_gradle_build_environment/FileUtils.kt
@@ -22,9 +22,23 @@ object FileUtils {
private const val PREF_NAME = "tree_uri_prefs"
const val ADDONS_DIR_NAME = "addons"
+ fun tryCopyDirectory(sourceDir: File, destDir: File): Boolean {
+ if (!sourceDir.isDirectory) {
+ Log.e(TAG, "Source directory ${sourceDir.absolutePath} not found")
+ return false
+ }
+ try {
+ sourceDir.copyRecursively(destDir, overwrite = true)
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to copy: ${e.message}")
+ return false
+ }
+ return true
+ }
+
fun tryCopyFile(source: File, dest: File): Boolean {
try {
- source.copyTo(dest)
+ source.copyTo(dest, overwrite = true)
} catch (e: Exception) {
Log.e(TAG, "Failed to copy ${source.absolutePath} to ${dest.absolutePath}: ${e.message}", e)
return false
diff --git a/app/src/main/java/org/godotengine/godot_gradle_build_environment/MainActivity.kt b/app/src/main/java/org/godotengine/godot_gradle_build_environment/MainActivity.kt
index be5eb07..b03798c 100644
--- a/app/src/main/java/org/godotengine/godot_gradle_build_environment/MainActivity.kt
+++ b/app/src/main/java/org/godotengine/godot_gradle_build_environment/MainActivity.kt
@@ -2,15 +2,20 @@ package org.godotengine.godot_gradle_build_environment
import android.Manifest
import android.app.AlertDialog
+import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
+import android.os.Environment
+import android.provider.Settings
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
+import androidx.annotation.RequiresApi
import org.godotengine.godot_gradle_build_environment.ui.theme.GodotGradleBuildEnvironmentTheme
+import androidx.core.net.toUri
class MainActivity : ComponentActivity() {
private val permissionRequestLauncher =
@@ -22,12 +27,41 @@ class MainActivity : ComponentActivity() {
}
}
+ @RequiresApi(Build.VERSION_CODES.R)
+ private val manageStorageLauncher =
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+ if (Environment.isExternalStorageManager()) {
+ Toast.makeText(this, "Storage Access Granted", Toast.LENGTH_SHORT).show()
+ } else {
+ Toast.makeText(this, "Storage Access Denied", Toast.LENGTH_SHORT).show()
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
Utils.createNotificationChannel(this)
+ if (BuildConfig.FLAVOR == "picoos" || BuildConfig.FLAVOR == "horizonos") {
+ requestManageStoragePermission()
+ } else {
+ requestNotificationPermission()
+ }
+
+ setContent {
+ GodotGradleBuildEnvironmentTheme {
+ MainScreen(
+ this,
+ AppPaths.getRootfs(this),
+ AppPaths.getRootfsReadyFile(this),
+ SettingsManager(this),
+ )
+ }
+ }
+ }
+
+ private fun requestNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
AlertDialog.Builder(this)
@@ -41,15 +75,19 @@ class MainActivity : ComponentActivity() {
.show()
}
}
+ }
- setContent {
- GodotGradleBuildEnvironmentTheme {
- MainScreen(
- this,
- AppPaths.getRootfs(this),
- AppPaths.getRootfsReadyFile(this),
- SettingsManager(this),
- )
+ private fun requestManageStoragePermission() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ if (!Environment.isExternalStorageManager()) {
+ val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply {
+ data = "package:${packageName}".toUri()
+ }
+ manageStorageLauncher.launch(intent)
+ }
+ } else {
+ if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ permissionRequestLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}
}
diff --git a/app/src/main/java/org/godotengine/godot_gradle_build_environment/ProjectInfo.kt b/app/src/main/java/org/godotengine/godot_gradle_build_environment/ProjectInfo.kt
index 8e63cdc..8a66e2d 100644
--- a/app/src/main/java/org/godotengine/godot_gradle_build_environment/ProjectInfo.kt
+++ b/app/src/main/java/org/godotengine/godot_gradle_build_environment/ProjectInfo.kt
@@ -36,6 +36,14 @@ data class ProjectInfo(
file.writeText(jsonString)
}
+ fun writeToDirectoryXR(directory: File, projectPath: String, gradleBuildDir: String) {
+ val name = File(projectPath).name
+ val projectInfo = ProjectInfo(projectPath, gradleBuildDir, "", name)
+ val jsonString = json.encodeToString(projectInfo)
+ val file = File(directory, PROJECT_INFO_FILENAME)
+ file.writeText(jsonString)
+ }
+
fun readFromDirectory(directory: File): ProjectInfo? {
val file = File(directory, PROJECT_INFO_FILENAME)
return if (file.exists()) {
diff --git a/app/src/picoos/AndroidManifest.xml b/app/src/picoos/AndroidManifest.xml
new file mode 100644
index 0000000..49ef05b
--- /dev/null
+++ b/app/src/picoos/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+