Skip to content

Commit 5d14e3e

Browse files
authored
Merge pull request #81 from groot007/dev
Add device info modal and Database folder separation
2 parents a38fa02 + 7703e7e commit 5d14e3e

16 files changed

Lines changed: 898 additions & 31 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "flippio",
3-
"version": "0.3.19",
3+
"version": "0.3.20",
44
"description": "Database viewer with device connection",
55
"author": "koliastanis",
66
"homepage": "https://github.com/groot007/flippio",

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "Flippio"
3-
version = "0.3.19"
3+
version = "0.3.20"
44
description = "Database viewer with device connection"
55
authors = ["koliastanis"]
66
edition = "2021"

src-tauri/src/commands/device/adb.rs

Lines changed: 113 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,10 @@ async fn pull_android_db_file(
2222
let temp_dir = ensure_temp_dir()?;
2323
info!("Temp directory: {:?}", temp_dir);
2424

25-
let filename = Path::new(remote_path).file_name()
26-
.ok_or("Invalid remote path")?
27-
.to_string_lossy();
28-
let local_path = temp_dir.join(&*filename);
29-
info!("Local path will be: {:?}", local_path);
25+
// Generate unique filename to avoid conflicts when multiple files have the same name
26+
let unique_filename = generate_unique_filename(remote_path)?;
27+
let local_path = temp_dir.join(&unique_filename);
28+
info!("Local path will be: {:?} (unique filename: {})", local_path, unique_filename);
3029

3130
// Execute ADB command based on admin access
3231
if admin_access {
@@ -449,7 +448,115 @@ pub async fn adb_push_database_file(
449448
success: false,
450449
data: None,
451450
error: Some(format!("Failed to push database file: {}", e)),
452-
}),
451+
})
452+
}
453+
}
454+
455+
// Get detailed Android device information using adb shell getprop
456+
async fn get_android_device_info(device_id: &str) -> Result<std::collections::HashMap<String, String>, Box<dyn std::error::Error + Send + Sync>> {
457+
info!("Getting Android device info for device: {}", device_id);
458+
459+
let output = execute_adb_command(&["-s", device_id, "shell", "getprop"]).await?;
460+
461+
info!("ADB getprop exit status: {:?}", output.status);
462+
463+
if !output.status.success() {
464+
let stderr = String::from_utf8_lossy(&output.stderr);
465+
error!("ADB getprop command failed. Stderr: {}", stderr);
466+
return Err(format!("ADB getprop failed with exit code: {:?}. Stderr: {}", output.status.code(), stderr).into());
467+
}
468+
469+
let stdout = String::from_utf8_lossy(&output.stdout);
470+
info!("ADB getprop output length: {} characters", stdout.len());
471+
472+
let mut device_info = std::collections::HashMap::new();
473+
let mut processed_lines = 0;
474+
475+
// Parse getprop output and extract key device information
476+
for line in stdout.lines() {
477+
if line.starts_with('[') && line.contains("]: [") {
478+
if let Some(key_end) = line.find("]: [") {
479+
let key = &line[1..key_end];
480+
if let Some(value_start) = line.rfind("]: [") {
481+
let value_part = &line[value_start + 4..];
482+
if let Some(value_end) = value_part.rfind(']') {
483+
let value = &value_part[..value_end];
484+
485+
// Only include relevant device info properties
486+
match key {
487+
"ro.product.model" => {
488+
device_info.insert("Device Model".to_string(), value.to_string());
489+
info!("Found device model: {}", value);
490+
},
491+
"ro.product.brand" => {
492+
device_info.insert("Brand".to_string(), value.to_string());
493+
info!("Found brand: {}", value);
494+
},
495+
"ro.product.manufacturer" => { device_info.insert("Manufacturer".to_string(), value.to_string()); },
496+
"ro.build.version.release" => {
497+
device_info.insert("Android Version".to_string(), value.to_string());
498+
info!("Found Android version: {}", value);
499+
},
500+
"ro.build.version.sdk" => { device_info.insert("SDK Version".to_string(), value.to_string()); },
501+
"ro.build.display.id" => { device_info.insert("Build ID".to_string(), value.to_string()); },
502+
"ro.product.cpu.abi" => { device_info.insert("CPU Architecture".to_string(), value.to_string()); },
503+
"ro.build.date" => { device_info.insert("Build Date".to_string(), value.to_string()); },
504+
"ro.product.device" => { device_info.insert("Device Codename".to_string(), value.to_string()); },
505+
"ro.build.version.security_patch" => { device_info.insert("Security Patch".to_string(), value.to_string()); },
506+
_ => {}
507+
}
508+
processed_lines += 1;
509+
}
510+
}
511+
}
512+
}
513+
}
514+
515+
info!("Processed {} lines from getprop output", processed_lines);
516+
517+
// Add device ID
518+
device_info.insert("Device ID".to_string(), device_id.to_string());
519+
520+
info!("Successfully retrieved {} device properties", device_info.len());
521+
522+
if device_info.len() <= 1 {
523+
// Only device ID was added, no properties found
524+
error!("No device properties found in getprop output");
525+
return Err("No device properties could be retrieved from the device".into());
526+
}
527+
528+
Ok(device_info)
529+
}
530+
531+
// Get detailed Android device information
532+
#[tauri::command]
533+
pub async fn adb_get_device_info(device_id: String) -> Result<DeviceResponse<std::collections::HashMap<String, String>>, String> {
534+
log::info!("Getting device info for Android device: {}", device_id);
535+
536+
match get_android_device_info(&device_id).await {
537+
Ok(info) => {
538+
log::info!("Successfully retrieved device info with {} properties", info.len());
539+
Ok(DeviceResponse {
540+
success: true,
541+
data: Some(info),
542+
error: None,
543+
})
544+
},
545+
Err(e) => {
546+
log::error!("Failed to get device info: {}", e);
547+
548+
// Return mock data for testing if real command fails
549+
let mut mock_info = std::collections::HashMap::new();
550+
mock_info.insert("Device ID".to_string(), device_id.clone());
551+
mock_info.insert("Status".to_string(), "Mock Data - Real command failed".to_string());
552+
mock_info.insert("Error".to_string(), format!("{}", e));
553+
554+
Ok(DeviceResponse {
555+
success: true,
556+
data: Some(mock_info),
557+
error: Some(format!("Using mock data - real command failed: {}", e)),
558+
})
559+
},
453560
}
454561
}
455562

src-tauri/src/commands/device/helpers.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,57 @@
11
use std::fs;
22
use std::path::{Path, PathBuf};
33
use log::{info, error};
4+
use std::collections::hash_map::DefaultHasher;
5+
use std::hash::{Hash, Hasher};
46

57
// Temp directory utilities
68
pub fn get_temp_dir_path() -> PathBuf {
79
std::env::temp_dir().join("flippio-db-temp")
810
}
911

12+
/// Generate a unique local filename based on remote path to avoid conflicts
13+
/// when multiple files have the same name but come from different device locations
14+
pub fn generate_unique_filename(remote_path: &str) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
15+
let path = Path::new(remote_path);
16+
let filename = path.file_name()
17+
.ok_or("Invalid remote path: no filename")?
18+
.to_string_lossy();
19+
20+
// Get the parent directory for uniqueness
21+
let parent_dir = path.parent()
22+
.map(|p| p.to_string_lossy())
23+
.unwrap_or_default();
24+
25+
// Create a short hash of the full path for uniqueness
26+
let mut hasher = DefaultHasher::new();
27+
remote_path.hash(&mut hasher);
28+
let path_hash = hasher.finish();
29+
30+
// Extract meaningful parent folder name for readability
31+
let parent_suffix = if !parent_dir.is_empty() {
32+
// Get the last meaningful directory component
33+
let path_parts: Vec<&str> = parent_dir.split('/').filter(|s| !s.is_empty()).collect();
34+
if let Some(last_dir) = path_parts.last() {
35+
format!("_{}", last_dir)
36+
} else {
37+
String::new()
38+
}
39+
} else {
40+
String::new()
41+
};
42+
43+
// Handle files with and without extensions
44+
if let Some(stem) = path.file_stem().map(|s| s.to_string_lossy()) {
45+
if let Some(ext) = path.extension().map(|s| s.to_string_lossy()) {
46+
Ok(format!("{}{}_{:x}.{}", stem, parent_suffix, path_hash, ext))
47+
} else {
48+
Ok(format!("{}{}_{:x}", stem, parent_suffix, path_hash))
49+
}
50+
} else {
51+
Ok(format!("{}_{:x}", filename, path_hash))
52+
}
53+
}
54+
1055
pub fn ensure_temp_dir() -> Result<PathBuf, Box<dyn std::error::Error + Send + Sync>> {
1156
let temp_dir = get_temp_dir_path();
1257

0 commit comments

Comments
 (0)