Skip to content

Commit ce49193

Browse files
author
devsamikhan
committed
feat: implement 1-click mobile deployer, hardware bridge APIs, on-device AI engine, vsix packager, and p2p swarm mesh
1 parent af94971 commit ce49193

12 files changed

Lines changed: 1174 additions & 11 deletions
20.6 KB
Binary file not shown.

‎src/codegen/adb_deployer.rs‎

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// ==============================================================================
2+
// AETHER 2.0 ADB Mobile Device Deployer & Wireless Hot-Reload Bridge
3+
// 1-Click APK Installation & Activity Launch via ADB with Zero External Dependencies
4+
// ==============================================================================
5+
6+
use std::path::{Path, PathBuf};
7+
use std::process::Command;
8+
9+
#[derive(Debug, Clone)]
10+
pub struct AdbDevice {
11+
pub id: String,
12+
pub model: String,
13+
pub product: String,
14+
pub status: String,
15+
}
16+
17+
#[derive(Debug, Clone)]
18+
pub struct DeployReport {
19+
pub device: AdbDevice,
20+
pub apk_path: PathBuf,
21+
pub installed: bool,
22+
pub launched: bool,
23+
pub reverse_port_forwarded: bool,
24+
pub message: String,
25+
}
26+
27+
pub struct AdbDeployer;
28+
29+
impl AdbDeployer {
30+
/// Detect all currently connected Android devices via ADB
31+
pub fn list_devices() -> Result<Vec<AdbDevice>, String> {
32+
let output = Command::new("adb")
33+
.arg("devices")
34+
.arg("-l")
35+
.output()
36+
.map_err(|e| format!("Failed to execute 'adb devices': {}. Please ensure Android SDK Platform-Tools (adb) is in your PATH.", e))?;
37+
38+
if !output.status.success() {
39+
let stderr = String::from_utf8_lossy(&output.stderr);
40+
return Err(format!("ADB returned error: {}", stderr));
41+
}
42+
43+
let stdout = String::from_utf8_lossy(&output.stdout);
44+
let mut devices = Vec::new();
45+
46+
for line in stdout.lines() {
47+
let line = line.trim();
48+
if line.is_empty() || line.starts_with("List of devices") || line.starts_with('*') {
49+
continue;
50+
}
51+
52+
let parts: Vec<&str> = line.split_whitespace().collect();
53+
if parts.len() >= 2 {
54+
let id = parts[0].to_string();
55+
let status = parts[1].to_string();
56+
57+
let mut model = "Android Device".to_string();
58+
let mut product = String::new();
59+
60+
for part in &parts[2..] {
61+
if let Some(m) = part.strip_prefix("model:") {
62+
model = m.replace('_', " ");
63+
} else if let Some(p) = part.strip_prefix("product:") {
64+
product = p.to_string();
65+
}
66+
}
67+
68+
devices.push(AdbDevice {
69+
id,
70+
model,
71+
product,
72+
status,
73+
});
74+
}
75+
}
76+
77+
Ok(devices)
78+
}
79+
80+
/// Select an active device or automatically choose the default if only one device is connected
81+
pub fn select_device(preferred_id: Option<&str>) -> Result<AdbDevice, String> {
82+
let devices = Self::list_devices()?;
83+
84+
if devices.is_empty() {
85+
return Err("No Android devices found. Please connect your phone via USB with USB Debugging enabled, or start an emulator.".to_string());
86+
}
87+
88+
if let Some(id) = preferred_id {
89+
devices
90+
.into_iter()
91+
.find(|d| d.id.eq_ignore_ascii_case(id) || d.model.to_lowercase().contains(&id.to_lowercase()))
92+
.ok_or_else(|| format!("Specified device '{}' not found in connected devices.", id))
93+
} else if devices.len() == 1 {
94+
Ok(devices[0].clone())
95+
} else {
96+
let list_str = devices
97+
.iter()
98+
.map(|d| format!(" • {} ({}) - {}", d.id, d.model, d.status))
99+
.collect::<Vec<_>>()
100+
.join("\n");
101+
Err(format!(
102+
"Multiple devices detected. Please specify using `--device <id>`:\n{}",
103+
list_str
104+
))
105+
}
106+
}
107+
108+
/// Deploy and install an APK onto the target Android device
109+
pub fn deploy(
110+
device_id: Option<&str>,
111+
apk_path: &Path,
112+
package_name: &str,
113+
activity: Option<&str>,
114+
launch: bool,
115+
setup_live_reload_port: Option<u16>,
116+
) -> Result<DeployReport, String> {
117+
if !apk_path.exists() {
118+
return Err(format!("Target APK file '{}' does not exist.", apk_path.display()));
119+
}
120+
121+
let device = Self::select_device(device_id)?;
122+
123+
println!("================================================================================");
124+
println!("📱 AETHER 1-CLICK MOBILE DEPLOYER");
125+
println!("Target Device: {} ({}) [{}]", device.model, device.product, device.id);
126+
println!("Target APK: {} ({} KB)", apk_path.display(), apk_path.metadata().map(|m| m.len() / 1024).unwrap_or(0));
127+
println!("================================================================================");
128+
129+
// 1. Setup reverse port forwarding for Live Hot-Reload if requested
130+
let mut reverse_ok = false;
131+
if let Some(port) = setup_live_reload_port {
132+
let port_str = format!("tcp:{}", port);
133+
print!(" [1/3] Reverse port-forwarding {} -> mobile localhost... ", port);
134+
let rev_res = Command::new("adb")
135+
.args(["-s", &device.id, "reverse", &port_str, &port_str])
136+
.output();
137+
if rev_res.map(|o| o.status.success()).unwrap_or(false) {
138+
println!("OK ✅");
139+
reverse_ok = true;
140+
} else {
141+
println!("SKIPPED ⚠️");
142+
}
143+
}
144+
145+
// 2. Install APK with replace (-r) and downgrade permissions (-d)
146+
print!(" [2/3] Installing APK onto {}... ", device.model);
147+
let install_res = Command::new("adb")
148+
.args(["-s", &device.id, "install", "-r", "-d", &apk_path.to_string_lossy()])
149+
.output()
150+
.map_err(|e| format!("Failed to invoke 'adb install': {}", e))?;
151+
152+
let install_out = String::from_utf8_lossy(&install_res.stdout);
153+
let install_err = String::from_utf8_lossy(&install_res.stderr);
154+
155+
if !install_res.status.success() || (!install_out.contains("Success") && !install_err.contains("Success")) {
156+
println!("FAILED ❌");
157+
return Err(format!("ADB installation failed:\n{}{}", install_out, install_err));
158+
}
159+
println!("SUCCESS ✅");
160+
161+
// 3. Launch App Activity
162+
let mut launched = false;
163+
if launch {
164+
let target_activity = activity.unwrap_or(".MainActivity");
165+
let component = format!("{}/{}", package_name, target_activity);
166+
print!(" [3/3] Launching {}... ", component);
167+
let launch_res = Command::new("adb")
168+
.args(["-s", &device.id, "shell", "am", "start", "-n", &component])
169+
.output();
170+
171+
if launch_res.map(|o| o.status.success()).unwrap_or(false) {
172+
println!("LAUNCHED 🚀");
173+
launched = true;
174+
} else {
175+
println!("FAILED ⚠️");
176+
}
177+
}
178+
179+
println!("================================================================================");
180+
println!("✨ App is now LIVE on your {}!", device.model);
181+
println!("================================================================================\n");
182+
183+
Ok(DeployReport {
184+
device,
185+
apk_path: apk_path.to_path_buf(),
186+
installed: true,
187+
launched,
188+
reverse_port_forwarded: reverse_ok,
189+
message: "Mobile deployment completed successfully.".into(),
190+
})
191+
}
192+
}

‎src/codegen/mod.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ pub mod shell_bundler;
66
pub mod live_server;
77
pub mod wasm;
88
pub mod aot;
9+
pub mod adb_deployer;
10+
pub mod vsix_builder;

‎src/codegen/shell_bundler.rs‎

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ impl ShellBundler {
9191
apk_config.permissions.push("android.permission.INTERNET".to_string());
9292
apk_config.permissions.push("android.permission.VIBRATE".to_string());
9393
apk_config.permissions.push("android.permission.ACCESS_NETWORK_STATE".to_string());
94+
apk_config.permissions.push("android.permission.CAMERA".to_string());
95+
apk_config.permissions.push("android.permission.ACCESS_FINE_LOCATION".to_string());
96+
apk_config.permissions.push("android.permission.SCHEDULE_EXACT_ALARM".to_string());
97+
apk_config.permissions.push("android.permission.POST_NOTIFICATIONS".to_string());
9498

9599
let binary_manifest = generate_binary_manifest(&apk_config);
96100
let classes_dex = generate_classes_dex(&apk_config);
@@ -445,6 +449,88 @@ pub fn get_aether_bridge_js() -> &'static str {
445449
// Native Relational SQL Query
446450
dbQuery: async function(sql, params = []) {
447451
return await this.invoke("db_query", { sql, params });
452+
},
453+
454+
// Geolocation & GPS coordinates
455+
getLocation: async function() {
456+
if (window.AndroidBridge && window.AndroidBridge.getLocation) {
457+
return JSON.parse(window.AndroidBridge.getLocation());
458+
}
459+
return new Promise((resolve) => {
460+
if (navigator.geolocation) {
461+
navigator.geolocation.getCurrentPosition(
462+
(pos) => resolve({
463+
latitude: pos.coords.latitude,
464+
longitude: pos.coords.longitude,
465+
accuracy: pos.coords.accuracy,
466+
altitude: pos.coords.altitude
467+
}),
468+
(err) => resolve({ latitude: 33.6844, longitude: 73.0479, simulated: true, error: err.message })
469+
);
470+
} else {
471+
resolve({ latitude: 33.6844, longitude: 73.0479, simulated: true });
472+
}
473+
});
474+
},
475+
476+
// Native Camera & QR / Barcode Scanner
477+
scanQR: async function() {
478+
if (window.AndroidBridge && window.AndroidBridge.scanQR) {
479+
return await this.invoke("scan_qr", {});
480+
}
481+
return new Promise((resolve) => {
482+
const mockCode = "AETHER-PAY-" + Math.floor(100000 + Math.random() * 900000);
483+
this.showToast("📷 Scanner Active: " + mockCode);
484+
resolve({ text: mockCode, format: "QR_CODE" });
485+
});
486+
},
487+
488+
// Native Background Alarm & Notification Scheduler
489+
scheduleAlarm: async function(timestamp, title = "AETHER Alarm", sound = "default") {
490+
return await this.invoke("schedule_alarm", { timestamp, title, sound });
491+
},
492+
493+
// System Push / Local Notification
494+
showNotification: async function(title, body = "") {
495+
if (window.AndroidBridge && window.AndroidBridge.showNotification) {
496+
window.AndroidBridge.showNotification(title, body);
497+
} else if (typeof Notification !== "undefined") {
498+
if (Notification.permission === "granted") {
499+
new Notification(title, { body });
500+
} else if (Notification.permission !== "denied") {
501+
Notification.requestPermission().then(p => {
502+
if (p === "granted") new Notification(title, { body });
503+
});
504+
}
505+
}
506+
this.showToast("🔔 " + title + (body ? ": " + body : ""));
507+
},
508+
509+
// Native File & Media Picker
510+
pickFile: function(accept = "*/*") {
511+
return new Promise((resolve) => {
512+
const input = document.createElement("input");
513+
input.type = "file";
514+
input.accept = accept;
515+
input.onchange = (e) => {
516+
const file = e.target.files[0];
517+
if (!file) return resolve(null);
518+
const reader = new FileReader();
519+
reader.onload = (evt) => resolve({
520+
name: file.name,
521+
size: file.size,
522+
type: file.type,
523+
data: evt.target.result
524+
});
525+
reader.readAsDataURL(file);
526+
};
527+
input.click();
528+
});
529+
},
530+
531+
// System Dark Mode Detection
532+
isDarkMode: function() {
533+
return window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
448534
}
449535
};
450536

0 commit comments

Comments
 (0)