pub mod insets;
use crate::adb::Device;
use crate::error::PalpError;
pub use insets::DeviceMetrics;
pub fn probe_device(device: &Device) -> Result<DeviceMetrics, PalpError> {
let wm_size_out = device.shell("wm size")?;
let (width, height) = parse_wm_size(&wm_size_out)?;
let wm_density_out = device.shell("wm density")?;
let density_dpi = parse_wm_density(&wm_density_out).unwrap_or(420);
let scale_factor = density_dpi as f32 / 160.0;
let dumpsys_out = device.shell("dumpsys window displays").unwrap_or_default();
let (status_bar_height, navigation_bar_height) =
parse_insets(&dumpsys_out, height, scale_factor);
Ok(DeviceMetrics {
width,
height,
density_dpi,
scale_factor,
status_bar_height,
navigation_bar_height,
})
}
fn parse_wm_size(output: &str) -> Result<(u32, u32), PalpError> {
for line in output.lines() {
let line = line.trim();
if let Some(idx) = line.find("size:") {
let part = line[idx + 5..].trim();
let mut dims = part.split('x');
if let (Some(w_str), Some(h_str)) = (dims.next(), dims.next()) {
if let (Ok(w), Ok(h)) = (w_str.trim().parse::<u32>(), h_str.trim().parse::<u32>()) {
return Ok((w, h));
}
}
}
}
Ok((1080, 2400))
}
fn parse_wm_density(output: &str) -> Option<u32> {
for line in output.lines() {
let line = line.trim();
if let Some(idx) = line.find("density:") {
let part = line[idx + 8..].trim();
if let Ok(dpi) = part.parse::<u32>() {
return Some(dpi);
}
}
}
None
}
fn parse_insets(dumpsys: &str, screen_height: u32, scale: f32) -> (u32, u32) {
let mut status_bar = None;
let mut nav_bar = None;
for line in dumpsys.lines() {
let line = line.trim();
if line.contains("type=statusBars") && line.contains("frame=[0,0][") {
if let Some(start) = line.find("frame=[0,0][") {
let rest = &line[start + 12..];
if let Some(end) = rest.find(']') {
let rect_str = &rest[..end]; let mut coords = rect_str.split(',');
let _w = coords.next();
if let Some(h_str) = coords.next() {
if let Ok(h) = h_str.trim().parse::<u32>() {
status_bar = Some(h);
}
}
}
}
}
if line.contains("type=navigationBars") && line.contains("frame=[0,") {
if let Some(start) = line.find("frame=[0,") {
let rest = &line[start + 9..];
if let Some(end) = rest.find(']') {
let top_y_str = &rest[..end];
if let Ok(top_y) = top_y_str.trim().parse::<u32>() {
if top_y < screen_height {
nav_bar = Some(screen_height - top_y);
}
}
}
}
}
}
let final_status = status_bar.unwrap_or_else(|| (48.0 * scale).round() as u32);
let final_nav = nav_bar.unwrap_or_else(|| (24.0 * scale).round() as u32);
(final_status, final_nav)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_wm_size() {
let raw = "Physical size: 1080x2392\n";
assert_eq!(parse_wm_size(raw).unwrap(), (1080, 2392));
}
#[test]
fn test_parse_wm_density() {
let raw = "Physical density: 420\n";
assert_eq!(parse_wm_density(raw), Some(420));
}
#[test]
fn test_parse_insets_dumpsys() {
let sample = "InsetsSource id=92400000 type=statusBars frame=[0,0][1080,126] visible=true flags= sideHint=TOP\n\
InsetsSource id=b0860001 type=navigationBars frame=[0,2329][1080,2392] visible=true flags= sideHint=BOTTOM";
let (sb, nb) = parse_insets(sample, 2392, 2.625);
assert_eq!(sb, 126);
assert_eq!(nb, 63);
}
}