diff --git a/CHANGELOG.md b/CHANGELOG.md index 2755f2c..45f402f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ and `Removed`. - Logfiles are now located in a more conventional place: [cache_dir](https://docs.rs/dirs/latest/dirs/). - Previous logs are no longer overwritten. The logger now only appends to the current logfile of the day (UAD_%Y%m%d.log). - Each new day the logger will create a new file on UAD launch. - +- [[#78](https://github.com/0x192/universal-android-debloater/issues/78)] Disable mode is now only available on Android 6+ because the disable ADB commands do not work without root on older devices. The setting will be greyed-out on older devices. ### Fixed - Miscellaneous minor issues in some package descriptions. diff --git a/src/gui/mod.rs b/src/gui/mod.rs index d51a4a6..ea0105a 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -152,7 +152,8 @@ impl Application for UadGui { ) .map(Message::AppsAction), Message::SettingsAction(msg) => { - self.settings_view.update(msg); + self.settings_view + .update(&self.selected_device.clone().unwrap_or_default(), msg); Command::none() } Message::AboutAction(msg) => { @@ -244,7 +245,10 @@ impl Application for UadGui { .about_view .view(&self.settings_view) .map(Message::AboutAction), - View::Settings => self.settings_view.view().map(Message::SettingsAction), + View::Settings => self + .settings_view + .view(&self.selected_device.clone().unwrap_or_default()) + .map(Message::SettingsAction), }; Column::new() diff --git a/src/gui/style.rs b/src/gui/style.rs index 0465523..1bbccdb 100644 --- a/src/gui/style.rs +++ b/src/gui/style.rs @@ -50,6 +50,33 @@ impl button::StyleSheet for PrimaryButton { } } +pub struct UnavailableButton(pub ColorPalette); +impl button::StyleSheet for UnavailableButton { + fn active(&self) -> button::Style { + button::Style { + border_color: Color { + a: 0.5, + ..self.0.bright.error + }, + border_width: 1.0, + border_radius: 2.0, + text_color: self.0.bright.error, + ..button::Style::default() + } + } + + fn hovered(&self) -> button::Style { + button::Style { + background: Some(Background::Color(Color { + a: 0.25, + ..self.0.normal.primary + })), + text_color: self.0.normal.error, + ..self.active() + } + } +} + pub struct RefreshButton(pub ColorPalette); impl button::StyleSheet for RefreshButton { fn active(&self) -> button::Style { @@ -293,7 +320,7 @@ impl checkbox::StyleSheet for SelectionCheckBox { border_radius: 5.0, border_width: 1.0, border_color: palette.normal.primary, - text_color: palette.bright.surface, + text_color: palette.normal.primary, }, } } @@ -316,27 +343,45 @@ impl checkbox::StyleSheet for SelectionCheckBox { } } -pub struct SettingsCheckbox(pub ColorPalette); -impl checkbox::StyleSheet for SettingsCheckbox { +pub enum SettingsCheckBox { + Enabled(ColorPalette), + Disabled(ColorPalette), +} + +impl checkbox::StyleSheet for SettingsCheckBox { fn active(&self, _is_checked: bool) -> checkbox::Style { - checkbox::Style { - background: Background::Color(self.0.base.background), - checkmark_color: self.0.bright.primary, - border_radius: 5.0, - border_width: 1.0, - border_color: self.0.bright.primary, - text_color: self.0.bright.surface, + match self { + Self::Enabled(palette) => checkbox::Style { + background: Background::Color(palette.base.background), + checkmark_color: palette.bright.primary, + border_radius: 5.0, + border_width: 1.0, + border_color: palette.bright.primary, + text_color: palette.bright.surface, + }, + Self::Disabled(palette) => checkbox::Style { + background: Background::Color(palette.base.foreground), + checkmark_color: palette.bright.primary, + border_radius: 5.0, + border_width: 1.0, + border_color: palette.normal.primary, + text_color: palette.normal.primary, + }, } } - - fn hovered(&self, _is_checked: bool) -> checkbox::Style { - checkbox::Style { - background: Background::Color(self.0.base.foreground), - checkmark_color: self.0.bright.primary, - border_radius: 5.0, - border_width: 2.0, - border_color: self.0.bright.primary, - text_color: self.0.bright.surface, + fn hovered(&self, is_checked: bool) -> checkbox::Style { + match self { + Self::Enabled(palette) => checkbox::Style { + background: Background::Color(palette.base.foreground), + checkmark_color: palette.bright.primary, + border_radius: 5.0, + border_width: 2.0, + border_color: palette.bright.primary, + text_color: palette.bright.surface, + }, + Self::Disabled(_) => checkbox::Style { + ..self.active(is_checked) + }, } } } diff --git a/src/gui/views/settings.rs b/src/gui/views/settings.rs index b3b26d0..25538fe 100644 --- a/src/gui/views/settings.rs +++ b/src/gui/views/settings.rs @@ -1,16 +1,21 @@ use crate::core::config::Config; -use crate::core::sync::get_android_sdk; +use crate::core::sync::{get_android_sdk, Phone as CorePhone}; use crate::core::theme::Theme; -use crate::core::utils::string_to_theme; +use crate::core::utils::{open_url, string_to_theme}; use crate::gui::style; use crate::IN_FILE_CONFIGURATION; -use iced::{pick_list, Checkbox, Column, Container, Element, Length, PickList, Space, Text}; +use iced::{ + button, pick_list, Button, Checkbox, Column, Container, Element, Length, PickList, Row, Space, + Text, +}; +use std::path::PathBuf; #[derive(Debug, Clone)] pub struct Settings { pub phone: Phone, pub theme: Theme, theme_picklist: pick_list::State, + unavailable_btn: button::State, } #[derive(Debug, Clone)] @@ -24,7 +29,7 @@ impl Default for Phone { fn default() -> Self { Self { expert_mode: false, - disable_mode: get_android_sdk() < 26, + disable_mode: false, multi_user_mode: get_android_sdk() > 21, } } @@ -36,6 +41,7 @@ impl Default for Settings { phone: Phone::default(), theme: string_to_theme(IN_FILE_CONFIGURATION.theme.clone()), theme_picklist: pick_list::State::default(), + unavailable_btn: button::State::default(), } } } @@ -46,10 +52,11 @@ pub enum Message { DisableMode(bool), MultiUserMode(bool), ApplyTheme(Theme), + UrlPressed(PathBuf), } impl Settings { - pub fn update(&mut self, msg: Message) { + pub fn update(&mut self, phone: &CorePhone, msg: Message) { match msg { Message::ExpertMode(toggled) => { info!( @@ -59,11 +66,13 @@ impl Settings { self.phone.expert_mode = toggled; } Message::DisableMode(toggled) => { - info!( - "Disable mode {}", - if toggled { "enabled" } else { "disabled" } - ); - self.phone.disable_mode = toggled; + if phone.android_sdk >= 23 { + info!( + "Disable mode {}", + if toggled { "enabled" } else { "disabled" } + ); + self.phone.disable_mode = toggled; + } } Message::MultiUserMode(toggled) => { info!( @@ -76,10 +85,13 @@ impl Settings { self.theme = theme; Config::save_changes(self); } + Message::UrlPressed(url) => { + open_url(url); + } } } - pub fn view(&mut self) -> Element { + pub fn view(&mut self, phone: &CorePhone) -> Element { let general_category_text = Text::new("General").size(25); let theme_picklist = PickList::new( @@ -102,18 +114,7 @@ impl Settings { "Allow to uninstall packages marked as \"unsafe\" (I KNOW WHAT I AM DOING)", Message::ExpertMode, ) - .style(style::SettingsCheckbox(self.theme.palette)); - - let disable_mode_descr = Text::new("Default mode on older phone (< Android 8.0) where uninstalled packages can't be restored.") - .size(15) - .color(self.theme.palette.normal.surface); - - let disable_mode_checkbox = Checkbox::new( - self.phone.disable_mode, - "Clear and disable packages instead of uninstalling them", - Message::DisableMode, - ) - .style(style::SettingsCheckbox(self.theme.palette)); + .style(style::SettingsCheckBox::Enabled(self.theme.palette)); let multi_user_mode_descr = Text::new("Disabling this setting will typically prevent affecting your work profile") @@ -125,7 +126,59 @@ impl Settings { "Affect all the users of the phone (not only the selected user)", Message::MultiUserMode, ) - .style(style::SettingsCheckbox(self.theme.palette)); + .style(style::SettingsCheckBox::Enabled(self.theme.palette)); + + let disable_color = if phone.android_sdk >= 23 { + self.theme.palette.normal.surface + } else { + self.theme.palette.normal.primary + }; + + let disable_checkbox_style = if phone.android_sdk >= 23 { + style::SettingsCheckBox::Enabled(self.theme.palette) + } else { + style::SettingsCheckBox::Disabled(self.theme.palette) + }; + + let disable_mode_descr = Text::new( + "In some cases, it can be better to disable a package instead of uninstalling it", + ) + .size(15) + .color(disable_color); + + let _unavailable_text = Text::new("[Unavailable before Android 8.0]") + .size(16) + .color(self.theme.palette.bright.error); + + let unavailable_btn = + Button::new(&mut self.unavailable_btn, Text::new("Unavailable").size(13)) + .on_press(Message::UrlPressed(PathBuf::from( + "https://github.com/0x192/universal-android-debloater/wiki/FAQ#\ + why-is-the-disable-mode-setting-not-available-for-my-device", + ))) + .height(Length::Units(22)) + .style(style::UnavailableButton(self.theme.palette)); + // Disabling package without root isn't really possible before Android Oreo (8.0) + // see https://github.com/0x192/universal-android-debloater/wiki/ADB-reference + let disable_mode_checkbox = Checkbox::new( + self.phone.disable_mode, + "Clear and disable packages instead of uninstalling them", + Message::DisableMode, + ) + .style(disable_checkbox_style); + + let disable_setting_row = if phone.android_sdk >= 23 { + Row::new() + .width(Length::Fill) + .push(disable_mode_checkbox) + .push(Space::new(Length::Fill, Length::Shrink)) + } else { + Row::new() + .width(Length::Fill) + .push(disable_mode_checkbox) + .push(Space::new(Length::Fill, Length::Shrink)) + .push(unavailable_btn) + }; let content = Column::new() .width(Length::Fill) @@ -138,11 +191,11 @@ impl Settings { .push(expert_mode_checkbox) .push(expert_mode_descr) .push(Space::new(Length::Fill, Length::Shrink)) - .push(disable_mode_checkbox) - .push(disable_mode_descr) - .push(Space::new(Length::Fill, Length::Shrink)) .push(multi_user_mode_checkbox) - .push(multi_user_mode_descr); + .push(multi_user_mode_descr) + .push(Space::new(Length::Fill, Length::Shrink)) + .push(disable_setting_row) + .push(disable_mode_descr); Container::new(content) .padding(10)