From 9ee0d99dd8f81ab4a2fed87ffedcde44750f15b3 Mon Sep 17 00:00:00 2001 From: Eric Ratliff Date: Sun, 1 Feb 2026 20:07:11 -0600 Subject: [PATCH] feat: add Android Studio integration (v1.1.0) Generate .idea/ run configurations for one-click build and deployment directly from Android Studio. Students can now open projects in the IDE they already know and hit the green play button to deploy to their robot. Run configurations generated: - Build: compiles APK without deploying (build.sh / build.bat) - Deploy (auto): auto-detects USB or WiFi connection - Deploy (USB): forces USB deployment (deploy.sh --usb) - Deploy (WiFi): forces WiFi deployment (deploy.sh --wifi) - Test: runs unit tests (./gradlew test) Both Unix (.sh) and Windows (.bat) variants are generated. Android Studio automatically hides the configurations whose script files don't exist, so only platform-appropriate configs appear in the Run dropdown. workspace.xml configures the project tree to hide internal directories (build/, .gradle/, gradle/) and expand src/ by default, giving students a clean view of just their code and the deployment scripts. Technical notes: - Uses ShConfigurationType (not the old ShellScript type) for Android Studio 2025.2+ compatibility - All paths use $PROJECT_DIR$ for portability - INTERPRETER_PATH is /bin/bash on Unix, cmd.exe on Windows - upgrade.rs regenerates all .idea/ files so run configs stay in sync with any future deploy.sh flag changes Requires Shell Script plugin (by JetBrains) to be installed in Android Studio. README.md updated with installation instructions. Files modified: - src/project/mod.rs: generate_idea_files() writes 5 XML files per platform - src/commands/upgrade.rs: add .idea/ files to safe_to_overwrite --- src/commands/upgrade.rs | 13 ++ src/project/mod.rs | 299 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 312 insertions(+) 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)?;