Parcourir la source

feat: android asset hotreloading (#3332)

Jonathan Kelley il y a 6 mois
Parent
commit
ca70e8660d

+ 20 - 1
packages/cli/src/serve/handle.rs

@@ -164,7 +164,7 @@ impl AppHandle {
     /// This will return the bundled name of the asset such that we can send it to the clients letting
     /// them know what to reload. It's not super important that this is robust since most clients will
     /// kick all stylsheets without necessarily checking the name.
-    pub(crate) fn hotreload_bundled_asset(&self, changed_file: &PathBuf) -> Option<PathBuf> {
+    pub(crate) async fn hotreload_bundled_asset(&self, changed_file: &PathBuf) -> Option<PathBuf> {
         let mut bundled_name = None;
 
         // Use the build dir if there's no runtime asset dir as the override. For the case of ios apps,
@@ -202,6 +202,25 @@ impl AppHandle {
             }
         }
 
+        // If the emulator is android, we need to copy the asset to the device with `adb push asset /data/local/tmp/dx/assets/filename.ext`
+        if self.app.build.build.platform() == Platform::Android {
+            if let Some(bundled_name) = bundled_name.as_ref() {
+                let target = format!("/data/local/tmp/dx/{}", bundled_name.display());
+                tracing::debug!("Pushing asset to device: {target}");
+                let res = tokio::process::Command::new("adb")
+                    .arg("push")
+                    .arg(changed_file)
+                    .arg(target)
+                    .output()
+                    .await
+                    .context("Failed to push asset to device");
+
+                if let Err(e) = res {
+                    tracing::debug!("Failed to push asset to device: {e}");
+                }
+            }
+        }
+
         // Now we can return the bundled asset name to send to the hotreload engine
         bundled_name
     }

+ 1 - 1
packages/cli/src/serve/mod.rs

@@ -91,7 +91,7 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> {
 
                 // if change is hotreloadable, hotreload it
                 // and then send that update to all connected clients
-                if let Some(hr) = runner.attempt_hot_reload(files) {
+                if let Some(hr) = runner.attempt_hot_reload(files).await {
                     // Only send a hotreload message for templates and assets - otherwise we'll just get a full rebuild
                     if hr.templates.is_empty()
                         && hr.assets.is_empty()

+ 2 - 2
packages/cli/src/serve/runner.rs

@@ -157,7 +157,7 @@ impl AppRunner {
         Ok(())
     }
 
-    pub(crate) fn attempt_hot_reload(
+    pub(crate) async fn attempt_hot_reload(
         &mut self,
         modified_files: Vec<PathBuf>,
     ) -> Option<HotReloadMsg> {
@@ -183,7 +183,7 @@ impl AppRunner {
 
             // Otherwise, it might be an asset and we should look for it in all the running apps
             for runner in self.running.values() {
-                if let Some(bundled_name) = runner.hotreload_bundled_asset(&path) {
+                if let Some(bundled_name) = runner.hotreload_bundled_asset(&path).await {
                     // todo(jon): don't hardcode this here
                     let asset_relative = PathBuf::from("/assets/").join(bundled_name);
                     assets.push(asset_relative);

+ 13 - 4
packages/desktop/src/protocol.rs

@@ -275,6 +275,19 @@ fn get_mime_by_ext(trimmed: &Path) -> &'static str {
 
 #[cfg(target_os = "android")]
 pub(crate) fn to_java_load_asset(filepath: &str) -> Option<Vec<u8>> {
+    let normalized = filepath
+        .trim_start_matches("/assets/")
+        .trim_start_matches('/');
+
+    // in debug mode, the asset might be under `/data/local/tmp/dx/` - attempt to read it from there if it exists
+    #[cfg(debug_assertions)]
+    {
+        let path = std::path::PathBuf::from("/data/local/tmp/dx/").join(normalized);
+        if path.exists() {
+            return std::fs::read(path).ok();
+        }
+    }
+
     use std::{io::Read, ptr::NonNull};
 
     let ctx = ndk_context::android_context();
@@ -301,10 +314,6 @@ pub(crate) fn to_java_load_asset(filepath: &str) -> Option<Vec<u8>> {
             NonNull::new(asset_manager).expect("Invalid asset manager"),
         );
 
-        let normalized = filepath
-            .trim_start_matches("/assets/")
-            .trim_start_matches('/');
-
         let cstr = std::ffi::CString::new(normalized).unwrap();
 
         let mut asset = asset_manager.open(&cstr)?;