-
-
Notifications
You must be signed in to change notification settings - Fork 961
/
app_info.rs
98 lines (86 loc) · 2.65 KB
/
app_info.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use std::path::Path;
use gtk::{
gio::{
content_type_guess, prelude::AppInfoExt, prelude::FileExt, AppInfo, AppLaunchContext,
DesktopAppInfo, File as GioFile, ResourceError,
},
glib::error::Error as GlibError,
};
use tokio::fs::File;
use tokio::io::AsyncReadExt;
thread_local! {
static LAUNCH_CTX: AppLaunchContext = {
// TODO: Display supports requires GDK, which can only run on the main thread
// let ctx = Display::default()
// .and_then(|display| display.app_launch_context())
// .map(|display| display.to_value().get::<AppLaunchContext>().expect(
// "This is an Glib type conversion, it should never fail because GDKAppLaunchContext is a subclass of AppLaunchContext"
// )).unwrap_or_default();
AppLaunchContext::default()
}
}
pub struct App {
pub id: String,
pub name: String,
// pub icon: Option<Vec<u8>>,
}
async fn recommended_for_type(file_path: impl AsRef<Path>) -> Vec<AppInfo> {
let data = if let Ok(mut file) = File::open(&file_path).await {
let mut data = [0; 1024];
if file.read_exact(&mut data).await.is_ok() {
Some(data)
} else {
None
}
} else {
None
};
let file_path = Some(file_path);
let (content_type, uncertain) = if let Some(data) = data {
content_type_guess(file_path, &data)
} else {
content_type_guess(file_path, &[])
};
if uncertain {
vec![]
} else {
AppInfo::recommended_for_type(content_type.as_str())
}
}
pub async fn list_apps_associated_with_ext(file_path: impl AsRef<Path>) -> Vec<App> {
recommended_for_type(file_path)
.await
.iter()
.flat_map(|app_info| {
app_info.id().map(|id| App {
id: id.to_string(),
name: app_info.name().to_string(),
// TODO: Icon supports requires GTK, which can only run on the main thread
// icon: app_info
// .icon()
// .and_then(|icon| {
// IconTheme::default().and_then(|icon_theme| {
// icon_theme.lookup_by_gicon(&icon, 128, IconLookupFlags::empty())
// })
// })
// .and_then(|icon_info| icon_info.load_icon().ok())
// .and_then(|pixbuf| pixbuf.save_to_bufferv("png", &[]).ok()),
})
})
.collect()
}
pub fn open_files_path_with(file_paths: &[impl AsRef<Path>], id: &str) -> Result<(), GlibError> {
let Some(app) = DesktopAppInfo::new(id) else {
return Err(GlibError::new(ResourceError::NotFound, "App not found"));
};
LAUNCH_CTX.with(|ctx| {
app.launch(
&file_paths.iter().map(GioFile::for_path).collect::<Vec<_>>(),
Some(ctx),
)
})
}
pub fn open_file_path(file_path: impl AsRef<Path>) -> Result<(), GlibError> {
let file_uri = GioFile::for_path(file_path).uri().to_string();
LAUNCH_CTX.with(|ctx| AppInfo::launch_default_for_uri(&file_uri.to_string(), Some(ctx)))
}