Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: device-specific persistent configuration #374

Merged
merged 3 commits into from Sep 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
The sections should follow the order `Apps`, `Added`, `Changed`, `Fixed`, `Packaging`
and `Removed`.

## [0.6] - Unreleased

### Added
- [[#374](https://github.com/0x192/universal-android-debloater/pull/374)] Device-specific persistent configuration. Some settings are now device-specific which means you can maintain different settings across several devices.
**Note: Settings specification has changed. Previous user settings will be erased**.

### Changed
- [[#374](https://github.com/0x192/universal-android-debloater/pull/374)] All settings are now persistent.

## [0.5.1] - 2022-07-03

Since `0.5.0`, all changes related to apps are available to users without downloading a new version of UAD as the software directly download the json debloat list from Github. These changes can be tracked in commits with `[Pkg]` in their name. [See the commits](https://github.com/0x192/universal-android-debloater/commits/main)
Expand Down
64 changes: 52 additions & 12 deletions src/core/config.rs
@@ -1,38 +1,78 @@
use crate::core::sync::get_android_sdk;
use crate::gui::views::settings::Settings;
use crate::CONFIG_DIR;
use serde::{Deserialize, Serialize};
use static_init::dynamic;
use std::fs;
use std::path::PathBuf;

#[derive(Debug, Serialize, Deserialize)]
#[derive(Default, Debug, Serialize, Deserialize, Clone)]
pub struct Config {
pub general: GeneralSettings,
#[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
pub devices: Vec<DeviceSettings>,
}

#[derive(Default, Debug, Serialize, Deserialize, Clone)]
pub struct GeneralSettings {
pub theme: String,
pub expert_mode: bool,
}

#[dynamic]
static CONFIG_FILE: PathBuf = CONFIG_DIR.join("config.toml");
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DeviceSettings {
pub device_id: String,
pub disable_mode: bool,
pub multi_user_mode: bool,
}

impl Default for Config {
impl Default for DeviceSettings {
fn default() -> Self {
Self {
theme: "Lupin".to_string(),
device_id: "".to_string(),
multi_user_mode: get_android_sdk() > 21,
disable_mode: false,
}
}
}

#[dynamic]
static CONFIG_FILE: PathBuf = CONFIG_DIR.join("config.toml");

impl Config {
pub fn save_changes(settings: &Settings) {
let new = Config {
theme: settings.theme.name.clone(),
};
let config = toml::to_string(&new).unwrap();
fs::write(&*CONFIG_FILE, config).expect("Could not write config file to disk!");
pub fn save_changes(settings: &Settings, device_id: &String) {
let mut config = Self::load_configuration_file();
match config
.devices
.iter_mut()
.find(|x| x.device_id == *device_id)
{
Some(device) => {
*device = settings.device.clone();
config.general = settings.general.clone();
}
None => {
debug!("config: New device settings saved");
config.devices.push(settings.device.clone());
config.general = settings.general.clone();
}
}
let toml = toml::to_string(&config).unwrap();
fs::write(&*CONFIG_FILE, toml).expect("Could not write config file to disk!");
}

pub fn load_configuration_file() -> Self {
match fs::read_to_string(&*CONFIG_FILE) {
Ok(s) => toml::from_str(&s).unwrap_or_else(|e| panic!("Invalid config file: `{}`", e)),
Ok(s) => match toml::from_str(&s) {
Ok(config) => config,
Err(e) => {
error!("Invalid config file: `{}`", e);
error!("Restoring default config file");
let toml = toml::to_string(&Config::default()).unwrap();
fs::write(&*CONFIG_FILE, toml).expect("Could not write config file to disk!");
Config::default()
}
},
Err(_) => {
let default_conf = toml::to_string(&Config::default()).unwrap();
fs::write(&*CONFIG_FILE, default_conf)
Expand Down
4 changes: 2 additions & 2 deletions src/core/sync.rs
@@ -1,6 +1,6 @@
use crate::core::config::DeviceSettings;
use crate::core::uad_lists::PackageState;
use crate::core::utils::request_builder;
use crate::gui::views::settings::Phone as SettingsPhone;
use crate::gui::widgets::package_row::PackageRow;
use regex::Regex;
use retry::{delay::Fixed, retry, OperationResult};
Expand Down Expand Up @@ -125,7 +125,7 @@ pub fn action_handler(
selected_u: &User,
package: &PackageRow,
phone: &Phone,
settings: &SettingsPhone,
settings: &DeviceSettings,
) -> Vec<String> {
// https://github.com/0x192/universal-android-debloater/wiki/ADB-reference
// ALWAYS PUT THE COMMAND THAT CHANGES THE PACKAGE STATE FIRST!
Expand Down
6 changes: 4 additions & 2 deletions src/gui/mod.rs
Expand Up @@ -6,7 +6,7 @@ use crate::core::sync::{get_devices_list, Phone};
use crate::core::theme::Theme;
use crate::core::uad_lists::UadListState;
use crate::core::update::{get_latest_release, Release, SelfUpdateState, SelfUpdateStatus};
use crate::core::utils::perform_commands;
use crate::core::utils::{perform_commands, string_to_theme};

use views::about::{About as AboutView, Message as AboutMessage};
use views::list::{List as AppsView, LoadingState as ListLoadingState, Message as AppsMessage};
Expand Down Expand Up @@ -83,7 +83,7 @@ impl Application for UadGui {
}

fn theme(&self) -> Theme {
self.settings_view.theme.clone()
string_to_theme(self.settings_view.general.theme.clone())
}

fn title(&self) -> String {
Expand All @@ -103,6 +103,7 @@ impl Application for UadGui {
None => devices_list.first().map(|x| x.to_owned()),
};
self.devices_list = devices_list;
self.update(Message::SettingsAction(SettingsMessage::LoadDeviceSettings));
self.update(Message::AppsAction(AppsMessage::LoadUadList(true)))
}
Message::AppsPress => {
Expand Down Expand Up @@ -193,6 +194,7 @@ impl Application for UadGui {
s_device.android_sdk, s_device.model
);
self.apps_view.loading_state = ListLoadingState::FindingPhones;
self.update(Message::SettingsAction(SettingsMessage::LoadDeviceSettings));
self.update(Message::AppsAction(AppsMessage::LoadPhonePackages((
self.apps_view.uad_lists.clone(),
UadListState::Done,
Expand Down
4 changes: 4 additions & 0 deletions src/gui/style.rs
Expand Up @@ -418,6 +418,7 @@ impl pick_list::StyleSheet for Theme {
#[derive(Clone, Copy)]
pub enum Text {
Default,
Danger,
Color(Color),
}

Expand All @@ -439,6 +440,9 @@ impl text::StyleSheet for Theme {
fn appearance(&self, style: Self::Style) -> text::Appearance {
match style {
Text::Default => Default::default(),
Text::Danger => text::Appearance {
color: Some(self.palette.bright.error),
},
Text::Color(c) => text::Appearance { color: Some(c) },
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/gui/views/list.rs
Expand Up @@ -174,7 +174,7 @@ impl List {

match row_message {
RowMessage::ToggleSelection(toggle) => {
if package.removal == Removal::Unsafe && !settings.phone.expert_mode {
if package.removal == Removal::Unsafe && !settings.general.expert_mode {
package.selected = false;
} else {
package.selected = toggle;
Expand All @@ -200,7 +200,7 @@ impl List {
&self.selected_user.unwrap(),
package,
selected_device,
&settings.phone,
&settings.device,
);

for (i, action) in actions.into_iter().enumerate() {
Expand Down Expand Up @@ -259,7 +259,7 @@ impl List {
&self.selected_user.unwrap(),
&self.phone_packages[i_user][i],
selected_device,
&settings.phone,
&settings.device,
);
for (j, action) in actions.into_iter().enumerate() {
// Only the first command can change the package state
Expand Down Expand Up @@ -314,14 +314,14 @@ impl List {
let package = &mut self.phone_packages[i_user][i];
update_selection_count(&mut self.selection, package.state, false);

if !settings.phone.multi_user_mode {
package.state = package.state.opposite(settings.phone.disable_mode);
if !settings.device.multi_user_mode {
package.state = package.state.opposite(settings.device.disable_mode);
package.selected = false;
} else {
for u in &selected_device.user_list {
self.phone_packages[u.index][i].state = self.phone_packages[u.index][i]
.state
.opposite(settings.phone.disable_mode);
.opposite(settings.device.disable_mode);
self.phone_packages[u.index][i].selected = false;
}
}
Expand Down Expand Up @@ -431,11 +431,11 @@ impl List {
.width(Length::Fill)
.style(style::Container::Description);

let restore_action = match settings.phone.disable_mode {
let restore_action = match settings.device.disable_mode {
true => "Enable/Restore",
false => "Restore",
};
let remove_action = match settings.phone.disable_mode {
let remove_action = match settings.device.disable_mode {
true => "Disable",
false => "Uninstall",
};
Expand Down