From 6931fb422f2e1acc68ae652107c80bdeda0c91c2 Mon Sep 17 00:00:00 2001 From: Ed B Date: Sun, 28 Apr 2024 17:08:41 +0300 Subject: [PATCH] feat: add ability to customize backup folder - added setting for back up folder - added pre-selected icons when importing icons.json to icommon - added Backup / Restore category when device is connected --- Cargo.lock | 265 ++++++++++++++++++++++++++++++++---- Cargo.toml | 1 + resources/assets/icons.json | 14 +- resources/assets/icons.ttf | Bin 1868 -> 1940 bytes src/core/config.rs | 14 +- src/core/save.rs | 5 +- src/core/utils.rs | 14 ++ src/gui/views/settings.rs | 85 ++++++++++-- 8 files changed, 357 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 06b6d6e..3fbd340 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,6 +141,24 @@ dependencies = [ "libloading 0.7.4", ] +[[package]] +name = "ashpd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" +dependencies = [ + "async-fs 2.1.1", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand", + "serde", + "serde_repr", + "url", + "zbus 4.1.2", +] + [[package]] name = "async-broadcast" version = "0.5.1" @@ -151,6 +169,18 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-broadcast" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb" +dependencies = [ + "event-listener 5.2.0", + "event-listener-strategy 0.5.0", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-channel" version = "2.2.0" @@ -190,6 +220,17 @@ dependencies = [ "futures-lite 1.13.0", ] +[[package]] +name = "async-fs" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" +dependencies = [ + "async-lock 3.3.0", + "blocking", + "futures-lite 2.3.0", +] + [[package]] name = "async-io" version = "1.13.0" @@ -249,6 +290,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io 2.3.2", + "blocking", + "futures-lite 2.3.0", +] + [[package]] name = "async-process" version = "1.8.1" @@ -266,6 +318,26 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "async-process" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53fc6301894e04a92cb2584fedde80cb25ba8e02d9dc39d4a87d036e22f397d" +dependencies = [ + "async-channel", + "async-io 2.3.2", + "async-lock 3.3.0", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.2.0", + "futures-lite 2.3.0", + "rustix 0.38.32", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "async-recursion" version = "1.1.0" @@ -829,8 +901,8 @@ dependencies = [ "rust-ini", "web-sys", "winreg", - "zbus", - "zvariant", + "zbus 3.15.1", + "zvariant 3.15.1", ] [[package]] @@ -979,6 +1051,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + [[package]] name = "enumflags2" version = "0.7.9" @@ -1986,7 +2064,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.4", ] [[package]] @@ -2204,6 +2282,19 @@ dependencies = [ "memoffset 0.7.1", ] +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "cfg_aliases 0.1.1", + "libc", + "memoffset 0.9.0", +] + [[package]] name = "num-traits" version = "0.2.18" @@ -2559,6 +2650,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2791,6 +2888,29 @@ dependencies = [ "rand", ] +[[package]] +name = "rfd" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" +dependencies = [ + "ashpd", + "block", + "dispatch", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + [[package]] name = "ring" version = "0.17.8" @@ -3488,6 +3608,7 @@ dependencies = [ "log", "regex", "retry", + "rfd", "serde", "serde_json", "static_init", @@ -3611,8 +3732,15 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "version_check" version = "0.9.4" @@ -4423,16 +4551,16 @@ checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" [[package]] name = "zbus" -version = "3.15.2" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" +checksum = "5acecd3f8422f198b1a2f954bcc812fe89f3fa4281646f3da1da7925db80085d" dependencies = [ - "async-broadcast", + "async-broadcast 0.5.1", "async-executor", - "async-fs", + "async-fs 1.6.0", "async-io 1.13.0", "async-lock 2.8.0", - "async-process", + "async-process 1.8.1", "async-recursion", "async-task", "async-trait", @@ -4445,7 +4573,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix", + "nix 0.26.4", "once_cell", "ordered-stream", "rand", @@ -4457,16 +4585,55 @@ dependencies = [ "uds_windows", "winapi", "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 3.15.1", + "zbus_names 2.6.1", + "zvariant 3.15.1", +] + +[[package]] +name = "zbus" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9ff46f2a25abd690ed072054733e0bc3157e3d4c45f41bd183dce09c2ff8ab9" +dependencies = [ + "async-broadcast 0.7.0", + "async-executor", + "async-fs 2.1.1", + "async-io 2.3.2", + "async-lock 3.3.0", + "async-process 2.2.2", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "derivative", + "enumflags2", + "event-listener 5.2.0", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.28.0", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros 4.1.2", + "zbus_names 3.0.0", + "zvariant 4.0.2", ] [[package]] name = "zbus_macros" -version = "3.15.2" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" +checksum = "2207eb71efebda17221a579ca78b45c4c5f116f074eb745c3a172e688ccf89f5" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -4476,6 +4643,20 @@ dependencies = [ "zvariant_utils", ] +[[package]] +name = "zbus_macros" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0e3852c93dcdb49c9462afe67a2a468f7bd464150d866e861eaf06208633e0" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + [[package]] name = "zbus_names" version = "2.6.1" @@ -4484,7 +4665,18 @@ checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" dependencies = [ "serde", "static_assertions", - "zvariant", + "zvariant 3.15.1", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant 4.0.2", ] [[package]] @@ -4530,23 +4722,37 @@ dependencies = [ [[package]] name = "zvariant" -version = "3.15.2" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" +checksum = "c5b4fcf3660d30fc33ae5cd97e2017b23a96e85afd7a1dd014534cd0bf34ba67" dependencies = [ "byteorder", "enumflags2", "libc", "serde", "static_assertions", - "zvariant_derive", + "zvariant_derive 3.15.1", +] + +[[package]] +name = "zvariant" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1b3ca6db667bfada0f1ebfc94b2b1759ba25472ee5373d4551bb892616389a" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive 4.0.2", ] [[package]] name = "zvariant_derive" -version = "3.15.2" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" +checksum = "0277758a8a0afc0e573e80ed5bfd9d9c2b48bd3108ffe09384f9f738c83f4a55" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -4556,10 +4762,23 @@ dependencies = [ ] [[package]] -name = "zvariant_utils" -version = "1.0.1" +name = "zvariant_derive" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +checksum = "b7a4b236063316163b69039f77ce3117accb41a09567fd24c168e43491e521bc" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 3fe13d5..c8cdcf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ dirs = "^5.0.0" ureq = { version = "*", features = ["json"] } retry = "^2.0.0" iced = { version = "^0.12.0", features = ["advanced", "image"] } +rfd = "^0.14" [target.'cfg(not(target_os = "windows"))'.dependencies] flate2 = { version = "^1", optional = true } diff --git a/resources/assets/icons.json b/resources/assets/icons.json index f554432..b3c80d2 100644 --- a/resources/assets/icons.json +++ b/resources/assets/icons.json @@ -416,8 +416,11 @@ { "ligatures": "folder-open, directory2", "name": "folder-open", - "order": 0, - "id": 49 + "order": 4, + "id": 49, + "prevSize": 28, + "code": 59696, + "tempChar": "" }, { "ligatures": "folder-plus, directory3", @@ -1016,8 +1019,11 @@ { "ligatures": "cog, gear", "name": "cog", - "order": 0, - "id": 149 + "order": 3, + "id": 149, + "prevSize": 28, + "code": 59796, + "tempChar": "" }, { "ligatures": "cogs, gears", diff --git a/resources/assets/icons.ttf b/resources/assets/icons.ttf index 4df2c688bd5735ba098bc8d8f2178fe93adbcdd4..e7f97f02d5ba2400160c3b4f5b6d574d4b94a64e 100644 GIT binary patch delta 400 zcmX@ZH-*2RfsuiMft#U$ftkU;KUm+0Ux;lJP-G7fCnV=47F=$*oW#JuC^_|NjF;fC^DW z82>*OXA!$9RwVixr~~N#%|(n2jD{=>%m;vcE(QiwVMS(PW00{I;b49q1Nsfj7-F=_n_3@m?u@@5%80ro@8Cm0x5O@Mrr zjNFn6!8{;~fi(h1$mHZFJ0>z_F*N`MN`L|axrr483_J{3Kz<95uaK9Rn>y2r^EZ&c z1ZYWHL4I)w(4j!Ut^<;1U}lb)c%a(c1IXcH;9y{4U<3L`;U&Y%DX)0`|NZ|Tq!fkE z`2Vpui&&xPFQ74uo2wWb7&jX+3o*%vRx~=s^V@u7;AR2qc_c5$31Wcg$qB6HjO>$V ku*yu{!OA1T=)|}SXbaE{3>gd%)l8Ec*@ZX1XI;t&0QYrGxc~qF diff --git a/src/core/config.rs b/src/core/config.rs index ed015f4..7715ec5 100644 --- a/src/core/config.rs +++ b/src/core/config.rs @@ -1,6 +1,7 @@ use crate::core::sync::{get_android_sdk, User}; use crate::core::utils::DisplayablePath; use crate::gui::views::settings::Settings; +use crate::CACHE_DIR; use crate::CONFIG_DIR; use serde::{Deserialize, Serialize}; use static_init::dynamic; @@ -14,10 +15,11 @@ pub struct Config { pub devices: Vec, } -#[derive(Default, Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct GeneralSettings { pub theme: String, pub expert_mode: bool, + pub backup_folder: PathBuf, } #[derive(Default, Debug, Clone)] @@ -38,6 +40,16 @@ pub struct DeviceSettings { pub backup: BackupSettings, } +impl Default for GeneralSettings { + fn default() -> Self { + Self { + theme: String::from("Dark"), + expert_mode: false, + backup_folder: CACHE_DIR.join("backups"), + } + } +} + impl Default for DeviceSettings { fn default() -> Self { Self { diff --git a/src/core/save.rs b/src/core/save.rs index 3a6697d..21f172e 100644 --- a/src/core/save.rs +++ b/src/core/save.rs @@ -1,4 +1,4 @@ -use crate::core::config::DeviceSettings; +use crate::core::config::{Config, DeviceSettings}; use crate::core::sync::{apply_pkg_state_commands, CorePackage, Phone, User}; use crate::core::utils::DisplayablePath; use crate::gui::widgets::package_row::PackageRow; @@ -51,7 +51,8 @@ pub async fn backup_phone( match serde_json::to_string_pretty(&backup) { Ok(json) => { - let backup_path = &*BACKUP_DIR.join(device_id); + let backup_dir: PathBuf = Config::load_configuration_file().general.backup_folder; + let backup_path = &*backup_dir.join(device_id); if let Err(e) = fs::create_dir_all(backup_path) { error!("BACKUP: could not create backup dir: {}", e); diff --git a/src/core/utils.rs b/src/core/utils.rs index b95d881..3e6b2d6 100644 --- a/src/core/utils.rs +++ b/src/core/utils.rs @@ -8,6 +8,11 @@ use std::path::PathBuf; use std::process::Command; use std::{fmt, fs}; +#[derive(Debug, Clone)] +pub enum Error { + DialogClosed(String), +} + pub fn fetch_packages(uad_lists: &PackageHashMap, user_id: Option<&User>) -> Vec { let all_system_packages = list_all_system_packages(user_id); // installed and uninstalled packages let enabled_system_packages = hashset_system_packages(PackageState::Enabled, user_id); @@ -129,3 +134,12 @@ impl fmt::Display for DisplayablePath { write!(f, "{stem}") } } + +pub async fn open_folder() -> Result { + let picked_folder = rfd::AsyncFileDialog::new() + .pick_folder() + .await + .ok_or(Error::DialogClosed(err))?; + + Ok(picked_folder.path().to_owned()) +} diff --git a/src/gui/views/settings.rs b/src/gui/views/settings.rs index 29b57a7..e7335e9 100644 --- a/src/gui/views/settings.rs +++ b/src/gui/views/settings.rs @@ -2,25 +2,29 @@ use crate::core::sync::AdbError; use crate::core::config::{BackupSettings, Config, DeviceSettings, GeneralSettings}; use crate::core::save::{ - backup_phone, list_available_backup_user, list_available_backups, restore_backup, BACKUP_DIR, + backup_phone, list_available_backup_user, list_available_backups, restore_backup, }; use crate::core::sync::{get_android_sdk, perform_adb_commands, CommandType, Phone}; use crate::core::theme::Theme; -use crate::core::utils::{open_url, string_to_theme, DisplayablePath}; +use crate::core::utils::{open_folder, open_url, string_to_theme, DisplayablePath}; use crate::gui::style; use crate::gui::views::list::PackageInfo; +use crate::gui::widgets::navigation_menu::ICONS; use crate::gui::widgets::package_row::PackageRow; use iced::widget::{ - button, checkbox, column, container, pick_list, radio, row, scrollable, text, Space, + button, checkbox, column, container, pick_list, radio, row, scrollable, text, Space, Text, }; use iced::{alignment, Alignment, Command, Element, Length, Renderer}; use std::path::PathBuf; +use crate::core::utils::Error; + #[derive(Debug, Clone)] pub struct Settings { pub general: GeneralSettings, pub device: DeviceSettings, + is_loading: bool, } impl Default for Settings { @@ -28,6 +32,7 @@ impl Default for Settings { Self { general: Config::load_configuration_file().general, device: DeviceSettings::default(), + is_loading: false, } } } @@ -45,6 +50,8 @@ pub enum Message { RestoreDevice, RestoringDevice(Result), DeviceBackedUp(Result<(), String>), + ChooseBackUpFolder, + FolderChosen(Result), } impl Settings { @@ -87,7 +94,8 @@ impl Settings { Command::none() } Message::LoadDeviceSettings => { - let backups = list_available_backups(&BACKUP_DIR.join(&phone.adb_id)); + let backups = + list_available_backups(&self.general.backup_folder.join(&phone.adb_id)); let backup = BackupSettings { backups: backups.clone(), selected: backups.first().cloned(), @@ -131,7 +139,7 @@ impl Settings { Message::DeviceBackedUp(_) => { info!("[BACKUP] Backup successfully created"); self.device.backup.backups = - list_available_backups(&BACKUP_DIR.join(phone.adb_id.clone())); + list_available_backups(&self.general.backup_folder.join(phone.adb_id.clone())); self.device.backup.selected = self.device.backup.backups.first().cloned(); Command::none() } @@ -178,6 +186,32 @@ impl Settings { }, // Trigger an action in mod.rs (Message::SettingsAction(msg)) Message::RestoringDevice(_) => Command::none(), + Message::FolderChosen(result) => { + self.is_loading = false; + + if let Ok(path) = result { + self.general.backup_folder = path; + Config::save_changes(self, &phone.adb_id); + #[allow(unused_must_use)] + { + self.update( + phone, + packages, + nb_running_async_adb_commands, + Message::LoadDeviceSettings, + ); + } + } + Command::none() + } + Message::ChooseBackUpFolder => { + if self.is_loading { + Command::none() + } else { + self.is_loading = true; + Command::perform(open_folder(), Message::FolderChosen) + } + } } } @@ -212,11 +246,37 @@ impl Settings { text("Most unsafe packages are known to bootloop the device if removed.") .style(style::Text::Commentary); - let general_ctn = container(column![expert_mode_checkbox, expert_mode_descr].spacing(10)) - .padding(10) - .width(Length::Fill) - .height(Length::Shrink) - .style(style::Container::Frame); + let choose_backup_descr = text("Note: If you have previous backups, you will need to transfer them manually to newly changed backup folder to be able to use Restore functionality") + .style(style::Text::Commentary); + + let choose_backup_btn = button(Text::new("\u{E930}").font(ICONS)) + .padding([5, 10]) + .on_press(Message::ChooseBackUpFolder) + .style(style::Button::Primary); + + let choose_backup_row = row![ + choose_backup_btn, + "Choose backup folder", + Space::new(Length::Fill, Length::Shrink), + "Current folder: ", + Text::new(self.general.backup_folder.to_string_lossy()) + ] + .spacing(10) + .align_items(Alignment::Center); + + let general_ctn = container( + column![ + expert_mode_checkbox, + expert_mode_descr, + choose_backup_row, + choose_backup_descr, + ] + .spacing(10), + ) + .padding(10) + .width(Length::Fill) + .height(Length::Shrink) + .style(style::Container::Frame); let warning_ctn = container( row![ @@ -346,7 +406,9 @@ impl Settings { .style(style::Button::Primary) } else { button("Open backup directory") - .on_press(Message::UrlPressed(BACKUP_DIR.join(phone.adb_id.clone()))) + .on_press(Message::UrlPressed( + self.general.backup_folder.join(phone.adb_id.clone()), + )) .padding([5, 10]) .style(style::Button::Primary) }; @@ -409,6 +471,7 @@ impl Settings { text("Current device").size(26), warning_ctn, device_specific_ctn, + text("Backup / Restore").size(26), backup_restore_ctn, ] .width(Length::Fill)