Skip to content

Commit

Permalink
feat: android asset hotreloading (#3332)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkelleyrtp authored Dec 11, 2024
1 parent 6a10fd8 commit ca70e86
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 8 deletions.
21 changes: 20 additions & 1 deletion packages/cli/src/serve/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/serve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/serve/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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);
Expand Down
17 changes: 13 additions & 4 deletions packages/desktop/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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)?;
Expand Down

0 comments on commit ca70e86

Please sign in to comment.