diff --git a/src/commands/upgrade.rs b/src/commands/upgrade.rs
index 328b4ae..c4bea1d 100644
--- a/src/commands/upgrade.rs
+++ b/src/commands/upgrade.rs
@@ -52,6 +52,19 @@ pub fn upgrade_project(path: &str) -> Result<()> {
"gradle/wrapper/gradle-wrapper.properties",
"gradle/wrapper/gradle-wrapper.jar",
".gitignore",
+ // Android Studio integration — regenerated so run configs stay in
+ // sync if deploy.sh flags or script names ever change.
+ ".idea/workspace.xml",
+ ".idea/runConfigurations/Build.xml",
+ ".idea/runConfigurations/Build (Windows).xml",
+ ".idea/runConfigurations/Deploy (auto).xml",
+ ".idea/runConfigurations/Deploy (auto) (Windows).xml",
+ ".idea/runConfigurations/Deploy (USB).xml",
+ ".idea/runConfigurations/Deploy (USB) (Windows).xml",
+ ".idea/runConfigurations/Deploy (WiFi).xml",
+ ".idea/runConfigurations/Deploy (WiFi) (Windows).xml",
+ ".idea/runConfigurations/Test.xml",
+ ".idea/runConfigurations/Test (Windows).xml",
];
println!("{}", "Updating infrastructure files...".bright_yellow());
diff --git a/src/project/mod.rs b/src/project/mod.rs
index 983341c..692d83d 100644
--- a/src/project/mod.rs
+++ b/src/project/mod.rs
@@ -55,6 +55,7 @@ impl ProjectBuilder {
"src/test/java/robot",
"src/test/java/robot/subsystems",
"gradle/wrapper",
+ ".idea/runConfigurations",
];
for dir in dirs {
@@ -416,9 +417,307 @@ class BasicTest {
test_file
)?;
+ // Android Studio integration: .idea/ files
+ self.generate_idea_files(project_path)?;
+
Ok(())
}
+ /// Generate .idea/ files for Android Studio integration.
+ ///
+ /// The goal is for students to open the project in Android Studio and see
+ /// a clean file tree (just src/ and the scripts) with Run configurations
+ /// that invoke Weevil's shell scripts directly. All the internal plumbing
+ /// (sdk/, .gradle/, build/) is hidden from the IDE view.
+ ///
+ /// Android Studio uses IntelliJ's run configuration XML format. The
+ /// ShellScript type invokes a script relative to the project root — exactly
+ /// what we want since deploy.sh and build.sh already live there.
+ fn generate_idea_files(&self, project_path: &Path) -> Result<()> {
+ // workspace.xml — controls the file-tree view and hides internals.
+ // We use a ProjectViewPane exclude pattern list rather than touching
+ // the module's source roots, so this works regardless of whether the
+ // student has opened the project before.
+ let workspace_xml = r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"#;
+ fs::write(project_path.join(".idea/workspace.xml"), workspace_xml)?;
+
+ // Run configurations. Each is a ShellScript type that invokes one of
+ // Weevil's scripts. Android Studio shows these in the Run dropdown
+ // at the top of the IDE — no configuration needed by the student.
+ //
+ // We generate both Unix (.sh, ./gradlew) and Windows (.bat, gradlew.bat)
+ // variants. Android Studio automatically hides configs whose script files
+ // don't exist, so only the platform-appropriate ones appear in the dropdown.
+
+ // Build (Unix) — just builds the APK without deploying
+ let build_unix_xml = r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"#;
+ fs::write(
+ project_path.join(".idea/runConfigurations/Build.xml"),
+ build_unix_xml,
+ )?;
+
+ // Build (Windows) — same, but calls build.bat
+ let build_windows_xml = r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"#;
+ fs::write(
+ project_path.join(".idea/runConfigurations/Build (Windows).xml"),
+ build_windows_xml,
+ )?;
+
+ // Deploy (auto) — no flags, deploy.sh auto-detects USB vs WiFi
+ let deploy_auto_xml = r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"#;
+ fs::write(
+ project_path.join(".idea/runConfigurations/Deploy (auto).xml"),
+ deploy_auto_xml,
+ )?;
+
+ // Deploy (auto) (Windows)
+ let deploy_auto_windows_xml = r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"#;
+ fs::write(
+ project_path.join(".idea/runConfigurations/Deploy (auto) (Windows).xml"),
+ deploy_auto_windows_xml,
+ )?;
+
+ // Deploy (USB) — forces USB connection
+ let deploy_usb_xml = r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"#;
+ fs::write(
+ project_path.join(".idea/runConfigurations/Deploy (USB).xml"),
+ deploy_usb_xml,
+ )?;
+
+ // Deploy (USB) (Windows)
+ let deploy_usb_windows_xml = r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"#;
+ fs::write(
+ project_path.join(".idea/runConfigurations/Deploy (USB) (Windows).xml"),
+ deploy_usb_windows_xml,
+ )?;
+
+ // Deploy (WiFi) — forces WiFi connection to default 192.168.43.1
+ let deploy_wifi_xml = r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"#;
+ fs::write(
+ project_path.join(".idea/runConfigurations/Deploy (WiFi).xml"),
+ deploy_wifi_xml,
+ )?;
+
+ // Deploy (WiFi) (Windows)
+ let deploy_wifi_windows_xml = r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"#;
+ fs::write(
+ project_path.join(".idea/runConfigurations/Deploy (WiFi) (Windows).xml"),
+ deploy_wifi_windows_xml,
+ )?;
+
+ // Test — runs the unit test suite via Gradle
+ let test_xml = r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"#;
+ fs::write(
+ project_path.join(".idea/runConfigurations/Test.xml"),
+ test_xml,
+ )?;
+
+ // Test (Windows)
+ let test_windows_xml = r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"#;
+ fs::write(
+ project_path.join(".idea/runConfigurations/Test (Windows).xml"),
+ test_windows_xml,
+ )?;
+
+
+ Ok(())
+ }
+
fn setup_gradle(&self, project_path: &Path) -> Result<()> {
println!("Setting up Gradle wrapper...");
crate::sdk::gradle::setup_wrapper(project_path)?;