From 18cefe3e809f3f26a8d450069e3b8445ac8ce4e6 Mon Sep 17 00:00:00 2001 From: w1nst0n Date: Sun, 3 Apr 2022 01:11:30 +0200 Subject: [PATCH] Better loading state management - Dedicated state for the UadList (Downloading, Done, Failed) - Some bugfixes introduced by previous commits (not in 0.4 release) --- src/core/uad_lists.rs | 73 +++++++++++++++++++++++++++++---------- src/gui/mod.rs | 65 ++++++++++++++++++++-------------- src/gui/views/about.rs | 4 +-- src/gui/views/list.rs | 50 ++++++++++++++++++--------- src/gui/views/settings.rs | 3 ++ 5 files changed, 132 insertions(+), 63 deletions(-) diff --git a/src/core/uad_lists.rs b/src/core/uad_lists.rs index f976d12..1fce25e 100644 --- a/src/core/uad_lists.rs +++ b/src/core/uad_lists.rs @@ -1,6 +1,6 @@ +use crate::core::utils::{format_diff_time_from_now, last_modified_date}; use crate::CACHE_DIR; -/*use chrono::DateTime; -use chrono::Utc;*/ +use retry::{delay::Fixed, retry, OperationResult}; use serde::Deserialize; use serde_json; use std::collections::HashMap; @@ -31,6 +31,31 @@ pub enum UadList { Unlisted, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum UadListState { + Downloading, + Done, + Failed, +} + +impl Default for UadListState { + fn default() -> Self { + UadListState::Downloading + } +} + +impl std::fmt::Display for UadListState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let date = last_modified_date(CACHE_DIR.join("uad_lists.json")); + let s = match self { + UadListState::Downloading => "Checking updates...".to_string(), + UadListState::Done => format!("Done (last was {})", format_diff_time_from_now(date)), + UadListState::Failed => "Failed to check update!".to_string(), + }; + write!(f, "{}", s) + } +} + impl Default for UadList { fn default() -> UadList { UadList::All @@ -172,26 +197,32 @@ impl std::fmt::Display for Removal { } } -pub async fn load_debloat_lists(remote: bool) -> HashMap { +pub async fn load_debloat_lists(remote: bool) -> (Result, ()>, bool) { let cached_uad_lists: PathBuf = CACHE_DIR.join("uad_lists.json"); - + let mut error = false; let list: Vec = if remote { - let req = ureq::get( - "https://raw.githubusercontent.com/0x192/universal-android-debloater/\ + match retry(Fixed::from_millis(500).take(120), || { + match ureq::get( + "https://raw.githubusercontent.com/0x192/universal-android-debloater/\ main/resources/assets/uad_lists.json", - ) - .call(); - - match req { - Ok(data) => { - let text = data.into_string().unwrap(); - fs::write(cached_uad_lists, &text).expect("Unable to write file"); - serde_json::from_str(&text).expect("Unable to parse") - } - Err(e) => { - warn!("Could not load remote debloat list: {}", e); - get_local_lists() + ) + .call() + { + Ok(data) => { + let text = data.into_string().unwrap(); + fs::write(cached_uad_lists.clone(), &text).expect("Unable to write file"); + let list = serde_json::from_str(&text).expect("Unable to parse"); + OperationResult::Ok(list) + } + Err(e) => { + warn!("Could not load remote debloat list: {}", e); + error = true; + OperationResult::Retry(Vec::::new()) + } } + }) { + Ok(list) => list, + Err(_) => vec![], } } else { warn!("Could not load remote debloat list"); @@ -204,7 +235,11 @@ pub async fn load_debloat_lists(remote: bool) -> HashMap { let name = p.id.clone(); package_lists.insert(name, p); } - package_lists + if error { + (Err(()), remote) + } else { + (Ok(package_lists), remote) + } } fn get_local_lists() -> Vec { diff --git a/src/gui/mod.rs b/src/gui/mod.rs index d123b14..3b377a0 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -3,8 +3,8 @@ pub mod views; pub mod widgets; pub use crate::core::sync::{get_device_list, Phone}; -use crate::core::uad_lists::load_debloat_lists; pub use crate::core::uad_lists::Package; +use crate::core::uad_lists::{load_debloat_lists, UadListState}; use crate::core::update::{get_latest_release, SelfUpdateState, SelfUpdateStatus}; use crate::core::utils::{icon, perform_commands}; use iced::pure::widget::Text; @@ -70,7 +70,7 @@ pub enum Message { SettingsAction(SettingsMessage), RefreshButtonPressed, RebootButtonPressed, - UadListsDownloaded(HashMap), + UadListsDownloaded((Result, ()>, bool)), InitList, InitDevice(Vec), _NewReleaseDownloaded(Result<(PathBuf, PathBuf), ()>), @@ -99,25 +99,41 @@ impl Application for UadGui { fn update(&mut self, message: Message) -> Command { match message { Message::InitList => { - debug!("Trying to download remote UAD list"); + warn!("Trying to download remote UAD list"); self.apps_view.state = ListState::Loading(ListLoadingState::DownloadingList); Command::perform(load_debloat_lists(true), Message::UadListsDownloaded) } - Message::UadListsDownloaded(uad_lists) => { - self.apps_view.uad_lists = uad_lists; - if self.ready { - Command::perform( - Self::load_phone_packages(self.selected_device.clone().unwrap_or_default()), - Message::AppsAction, - ) - } else { - self.ready = true; - self.apps_view.state = ListState::Loading(ListLoadingState::FindingPhones); + Message::UadListsDownloaded((uad_lists, _)) => match uad_lists { + Ok(list) => { + if !list.is_empty() { + self.apps_view.uad_lists = list; + } + self.settings_view.list_update_state = UadListState::Done; + + if self.ready { + Command::perform( + Self::load_phone_packages( + self.selected_device.clone().unwrap_or_default(), + ), + Message::AppsAction, + ) + } else { + self.ready = true; + self.apps_view.state = ListState::Loading(ListLoadingState::FindingPhones); + Command::none() + } + } + Err(_) => { + self.settings_view.list_update_state = UadListState::Failed; + if let ListState::Ready = self.apps_view.state { + } else { + self.apps_view.state = + ListState::Loading(ListLoadingState::DownloadingList); + } Command::none() } - } + }, Message::InitDevice(device_list) => { - self.apps_view.state = ListState::Loading(ListLoadingState::LoadingPackages); self.device_list = device_list; self.settings_view.phone = SettingsPhone::default(); @@ -141,7 +157,7 @@ impl Application for UadGui { } else { self.selected_device = None; } - if self.ready { + if self.settings_view.list_update_state != UadListState::Downloading || self.ready { Command::perform( Self::load_phone_packages(self.selected_device.clone().unwrap_or_default()), Message::AppsAction, @@ -160,14 +176,12 @@ impl Application for UadGui { Message::RebootButtonPressed => { self.apps_view.state = ListState::Loading(ListLoadingState::FindingPhones); self.selected_device = None; + self.device_list = vec![]; self.ready = false; - Command::batch([ - Command::perform( - perform_commands("reboot".to_string(), 0, "ADB".to_string()), - |_| Message::Nothing, - ), - Command::perform(get_device_list(), Message::InitDevice), - ]) + Command::perform( + perform_commands("reboot".to_string(), 0, "ADB".to_string()), + |_| Message::Nothing, + ) } Message::AppsPress => { self.view = View::List; @@ -185,7 +199,7 @@ impl Application for UadGui { Message::AppsAction(msg) => self .apps_view .update( - &self.settings_view.phone, + &mut self.settings_view, &mut self.selected_device.clone().unwrap_or_default(), msg, ) @@ -200,6 +214,7 @@ impl Application for UadGui { match msg { AboutMessage::UpdateUadLists => { + self.settings_view.list_update_state = UadListState::Downloading; Command::perform(Self::download_uad_list(), |_| Message::InitList) } AboutMessage::DoSelfUpdate => { @@ -288,7 +303,7 @@ impl Application for UadGui { } Command::none() } - Message::Nothing => Command::none(), + Message::Nothing => Command::perform(get_device_list(), Message::InitDevice), } } diff --git a/src/gui/views/about.rs b/src/gui/views/about.rs index 3898b8c..3558eca 100644 --- a/src/gui/views/about.rs +++ b/src/gui/views/about.rs @@ -1,4 +1,4 @@ -use crate::core::utils::{format_diff_time_from_now, last_modified_date, open_url}; +use crate::core::utils::{last_modified_date, open_url}; use crate::gui::style; use crate::gui::views::settings::Settings; use crate::CACHE_DIR; @@ -47,7 +47,7 @@ impl About { let date = last_modified_date(CACHE_DIR.join("uad_lists.json")); let uad_list_text = text(format!("Documentation: v{}", date.format("%Y%m%d"))).width(Length::Units(250)); - let last_update_text = text(format!("(last was {})", format_diff_time_from_now(date))) + let last_update_text = text(settings.list_update_state.to_string()) .color(settings.theme.palette.normal.surface); let uad_lists_btn = button("Update") .on_press(Message::UpdateUadLists) diff --git a/src/gui/views/list.rs b/src/gui/views/list.rs index f6bc422..09c8795 100644 --- a/src/gui/views/list.rs +++ b/src/gui/views/list.rs @@ -1,6 +1,6 @@ use crate::core::sync::{action_handler, Phone, User}; use crate::core::uad_lists::{ - load_debloat_lists, Opposite, Package, PackageState, Removal, UadList, + load_debloat_lists, Opposite, Package, PackageState, Removal, UadList, UadListState, }; use crate::core::utils::{ export_selection, fetch_packages, import_selection, perform_commands, update_selection_count, @@ -9,7 +9,7 @@ use crate::gui::style; use std::collections::HashMap; use std::env; -use crate::gui::views::settings::{Phone as SettingsPhone, Settings}; +use crate::gui::views::settings::Settings; use crate::gui::widgets::package_row::{Message as RowMessage, PackageRow}; use iced::pure::{ button, column, container, pick_list, row, scrollable, text, text_input, Element, @@ -70,7 +70,7 @@ pub enum Message { SearchInputChanged(String), ToggleAllSelected(bool), InitUadList(bool), - ListsIsInitialized(HashMap), + ListsIsInitialized((Result, ()>, bool)), LoadPackages, ListSelected(UadList), UserSelected(User), @@ -88,7 +88,7 @@ pub enum Message { impl List { pub fn update( &mut self, - settings: &SettingsPhone, + settings: &mut Settings, phone: &mut Phone, message: Message, ) -> Command { @@ -96,18 +96,30 @@ impl List { match message { Message::Nothing => Command::none(), Message::InitUadList(remote) => { - self.state = State::Loading(LoadingState::LoadingPackages); + self.state = State::Loading(LoadingState::FindingPhones); if remote { Command::perform(load_debloat_lists(true), Message::ListsIsInitialized) } else { Command::perform(load_debloat_lists(false), Message::ListsIsInitialized) } } - Message::ListsIsInitialized(uad_lists) => { - self.uad_lists = uad_lists; - env::set_var("ANDROID_SERIAL", phone.adb_id.clone()); - Command::perform(Self::do_load_packages(), |_| Message::LoadPackages) - } + Message::ListsIsInitialized((uad_lists, remote)) => match uad_lists { + Ok(list) => { + if !remote { + settings.list_update_state = UadListState::Failed; + } else { + settings.list_update_state = UadListState::Done; + } + self.uad_lists = list; + if !phone.adb_id.is_empty() { + env::set_var("ANDROID_SERIAL", phone.adb_id.clone()); + Command::perform(Self::do_load_packages(), |_| Message::LoadPackages) + } else { + Command::none() + } + } + Err(_) => Command::none(), + }, Message::LoadPackages => { self.state = State::Loading(LoadingState::LoadingPackages); self.selected_package_state = Some(PackageState::Enabled); @@ -186,7 +198,7 @@ impl List { match row_message { RowMessage::ToggleSelection(toggle) => { - if package.removal == Removal::Unsafe && !settings.expert_mode { + if package.removal == Removal::Unsafe && !settings.phone.expert_mode { package.selected = false; } else { package.selected = toggle; @@ -208,8 +220,12 @@ impl List { } RowMessage::ActionPressed => { let mut commands = vec![]; - let actions = - action_handler(&self.selected_user.unwrap(), package, phone, settings); + let actions = action_handler( + &self.selected_user.unwrap(), + package, + phone, + &settings.phone, + ); for (i, action) in actions.into_iter().enumerate() { // Only the first command can change the package state @@ -262,7 +278,7 @@ impl List { &self.selected_user.unwrap(), &self.phone_packages[*i_user][i], phone, - settings, + &settings.phone, ); for (j, action) in actions.into_iter().enumerate() { // Only the first command can change the package state @@ -317,14 +333,14 @@ impl List { let package = &mut self.phone_packages[*i_user][i]; update_selection_count(&mut self.selection, package.state, false); - if !settings.multi_user_mode { - package.state = package.state.opposite(settings.disable_mode); + if !settings.phone.multi_user_mode { + package.state = package.state.opposite(settings.phone.disable_mode); package.selected = false; } else { for u in &phone.user_list { self.phone_packages[u.index][i].state = self.phone_packages[u.index][i] .state - .opposite(settings.disable_mode); + .opposite(settings.phone.disable_mode); self.phone_packages[u.index][i].selected = false; } } diff --git a/src/gui/views/settings.rs b/src/gui/views/settings.rs index 698544d..4a15404 100644 --- a/src/gui/views/settings.rs +++ b/src/gui/views/settings.rs @@ -1,6 +1,7 @@ use crate::core::config::Config; use crate::core::sync::{get_android_sdk, Phone as CorePhone}; use crate::core::theme::Theme; +use crate::core::uad_lists::UadListState; use crate::core::update::{Release, SelfUpdateState, SelfUpdateStatus}; use crate::core::utils::{open_url, string_to_theme}; use crate::gui::style; @@ -16,6 +17,7 @@ pub struct Settings { pub phone: Phone, pub theme: Theme, pub self_update_state: SelfUpdateState, + pub list_update_state: UadListState, } #[derive(Debug, Clone)] @@ -41,6 +43,7 @@ impl Default for Settings { phone: Phone::default(), theme: string_to_theme(IN_FILE_CONFIGURATION.theme.clone()), self_update_state: SelfUpdateState::default(), + list_update_state: UadListState::default(), } } }