mirror of
https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation.git
synced 2026-08-26 07:24:19 +02:00
parent
7f0ad4bcc6
commit
2476a59423
5 changed files with 170 additions and 35 deletions
|
|
@ -14,14 +14,16 @@ and `Removed`.
|
|||
- UAD now comes with a logger. Debug information will be written to a `uad.log` file (Warning level log in *stdout*) (#2)
|
||||
- Support for older phone (< Android 8.0) (#15):
|
||||
- Disable mode in settings: clear and disable packages instead of uninstalling them (default for old phones because you can't restore uninstalled packages)
|
||||
- Export your selection in the `uad_exported_selection.txt` file. Packages from this file (if found in the current directory) will be automatically selected upon the start of UAD (or after a refresh) (#8)
|
||||
|
||||
### Changed
|
||||
- UAD will no longer crash at start if it doesn't find ADB but will display a useful message (#25)
|
||||
- UAD will no longer crash at start if it doesn't find ADB but will display a useful error message (#25)
|
||||
- Better handling of ADB errors (#3)
|
||||
- Updated dependencies (compatibility with [Iced](https://github.com/iced-rs/iced) main branch latest commit)
|
||||
- Cleanup and refactoring of the code
|
||||
- Performance improvement
|
||||
- Refresh settings panel
|
||||
- Various UI/UX improvement
|
||||
- The `Debloat/Restore selection` button has been split in 2 buttons: `removing` and `restoring`
|
||||
|
||||
### Fixed
|
||||
- Spelling mistake
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
pub mod sync;
|
||||
pub mod uad_lists;
|
||||
pub mod uad_lists;
|
||||
pub mod utils;
|
||||
58
src/core/utils.rs
Normal file
58
src/core/utils.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use crate::core::uad_lists::PackageState;
|
||||
use crate::gui::widgets::package_row::PackageRow;
|
||||
use crate::gui::views::list::Selection;
|
||||
|
||||
use std::fs;
|
||||
use std::io::{self, prelude::*, BufReader};
|
||||
|
||||
pub fn update_selection_count(selection: &mut Selection, p_state: PackageState, add: bool) {
|
||||
match p_state {
|
||||
PackageState::Enabled => {
|
||||
if add { selection.enabled += 1 } else { selection.enabled -= 1 };
|
||||
},
|
||||
PackageState::Disabled => {
|
||||
if add { selection.disabled += 1 } else { selection.disabled -= 1 };
|
||||
},
|
||||
PackageState::Uninstalled => {
|
||||
if add { selection.uninstalled += 1 } else { selection.uninstalled -= 1 };
|
||||
},
|
||||
PackageState::All => {}
|
||||
};
|
||||
}
|
||||
|
||||
pub async fn export_selection(packages: Vec<PackageRow>) -> Result<bool, String> {
|
||||
let selected = packages
|
||||
.iter()
|
||||
.filter(|p| p.selected)
|
||||
.map(|p| p.name.clone())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
match fs::write("uad_selection.txt", selected) {
|
||||
Ok(_) => Ok(true),
|
||||
Err(err) => Err(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import_selection(packages: &mut Vec<PackageRow>, selection: &mut Selection) -> io::Result<()> {
|
||||
let file = fs::File::open("uad_exported_selection.txt")?;
|
||||
let reader = BufReader::new(file);
|
||||
let imported_selection: Vec<String> = reader
|
||||
.lines()
|
||||
.map(|l| l.expect("Could not parse line"))
|
||||
.collect();
|
||||
|
||||
*selection = Selection::default(); // should already be empty normally
|
||||
|
||||
for (i,p) in packages.iter_mut().enumerate() {
|
||||
if imported_selection.contains(&p.name) {
|
||||
p.selected = true;
|
||||
selection.selected_packages.push(i);
|
||||
update_selection_count(selection, p.state, true);
|
||||
} else {
|
||||
p.selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -133,7 +133,9 @@ impl Application for UadGui {
|
|||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
let add_svg_path = format!("{}/ressources/assets/refresh.svg", env!("CARGO_MANIFEST_DIR"));
|
||||
let refresh_list_icon = Svg::from_path(add_svg_path);
|
||||
let refresh_list_icon = Svg::from_path(add_svg_path)
|
||||
.width(Length::Units(17))
|
||||
.height(Length::Units(17));
|
||||
|
||||
let apps_btn = Button::new(&mut self.apps_btn, Text::new("Apps"))
|
||||
.on_press(Message::AppsPress)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::gui::style;
|
||||
use crate::core::uad_lists::{ UadList, PackageState, Package, Removal, Opposite};
|
||||
use crate::core::utils::{import_selection, export_selection, update_selection_count};
|
||||
use std::{collections::HashMap};
|
||||
|
||||
use crate::gui::views::settings::Settings;
|
||||
use crate::gui::widgets::package_row::{ PackageRow, Message as RowMessage };
|
||||
|
||||
use iced::{
|
||||
scrollable, Alignment, alignment, Column, Container, Element, Space,
|
||||
Length, Row, Scrollable, Text, text_input, TextInput, Command,//Svg,
|
||||
|
|
@ -16,16 +16,42 @@ use crate::core::sync::{
|
|||
Phone, action_handler,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Selection {
|
||||
pub uninstalled: u16,
|
||||
pub enabled: u16,
|
||||
pub disabled: u16,
|
||||
pub selected_packages: Vec<usize>, // phone_packages indexes (= what you've selected)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Action {
|
||||
Remove,
|
||||
Restore,
|
||||
}
|
||||
|
||||
impl Default for Selection {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
uninstalled: 0,
|
||||
enabled: 0,
|
||||
disabled: 0,
|
||||
selected_packages: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct List {
|
||||
ready: bool,
|
||||
phone_packages: Vec<PackageRow>, // packages of the phone
|
||||
filtered_packages: Vec<usize>, // phone_packages indexes (= what you see on screen)
|
||||
selected_packages: Vec<usize>, // phone_packages indexes (= what you've selected)
|
||||
pub selection: Selection,
|
||||
search_input: text_input::State,
|
||||
select_all_btn_state: button::State,
|
||||
apply_selection_btn_state: button::State,
|
||||
export_selection_btn_state: button::State,
|
||||
apply_remove_selection: button::State,
|
||||
apply_restore_selection: button::State,
|
||||
package_scrollable_state: scrollable::State,
|
||||
package_state_picklist: pick_list::State<PackageState>,
|
||||
list_picklist: pick_list::State<UadList>,
|
||||
|
|
@ -45,9 +71,11 @@ pub enum Message {
|
|||
ListSelected(UadList),
|
||||
PackageStateSelected(PackageState),
|
||||
RemovalSelected(Removal),
|
||||
ApplyActionOnSelection,
|
||||
ApplyActionOnSelection(Action),
|
||||
SelectAllPressed,
|
||||
ExportSelectionPressed,
|
||||
List(usize, RowMessage),
|
||||
ExportedSelection(Result<bool, String>),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -98,6 +126,11 @@ impl List {
|
|||
self.phone_packages.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
||||
self.filtered_packages = (0..self.phone_packages.len()).collect();
|
||||
Self::filter_package_lists(self);
|
||||
|
||||
match import_selection(&mut self.phone_packages, &mut self.selection) {
|
||||
Ok(_) => info!("Custom selection has been successfully imported"),
|
||||
Err(err) => warn!("No custom selection imported: {}", err),
|
||||
};
|
||||
self.ready = true;
|
||||
Command::none()
|
||||
}
|
||||
|
|
@ -129,9 +162,11 @@ impl List {
|
|||
RowMessage::ToggleSelection(toggle) => {
|
||||
self.phone_packages[i].selected = toggle;
|
||||
if self.phone_packages[i].selected {
|
||||
self.selected_packages.push(i);
|
||||
self.selection.selected_packages.push(i);
|
||||
update_selection_count(&mut self.selection, self.phone_packages[i].state, true);
|
||||
} else {
|
||||
self.selected_packages.drain_filter(|s_i| *s_i == i);
|
||||
self.selection.selected_packages.drain_filter(|s_i| *s_i == i);
|
||||
update_selection_count(&mut self.selection, self.phone_packages[i].state, false);
|
||||
}
|
||||
},
|
||||
RowMessage::ActionPressed => {
|
||||
|
|
@ -146,9 +181,11 @@ impl List {
|
|||
.iter()
|
||||
.position(|p| p.name == self.phone_packages[i].name)
|
||||
.unwrap();
|
||||
|
||||
if self.phone_packages[i].selected {
|
||||
update_selection_count(&mut self.selection, self.phone_packages[i].state, false);
|
||||
}
|
||||
self.phone_packages[i].state = self.phone_packages[i].state.opposite(settings.disable_mode);
|
||||
self.selected_packages.drain_filter(|s_i| *s_i == i);
|
||||
self.selection.selected_packages.drain_filter(|s_i| *s_i == i);
|
||||
self.phone_packages[i].selected = false;
|
||||
Self::filter_package_lists(self);
|
||||
}
|
||||
|
|
@ -159,8 +196,19 @@ impl List {
|
|||
}
|
||||
Command::none()
|
||||
},
|
||||
Message::ApplyActionOnSelection => {
|
||||
for i in self.selected_packages.clone() {
|
||||
Message::ApplyActionOnSelection(action) => {
|
||||
let mut selected_packages = self.selection.selected_packages.clone();
|
||||
|
||||
match action {
|
||||
Action::Remove => {
|
||||
selected_packages.drain_filter(|i| self.phone_packages[*i].state != PackageState::Enabled);
|
||||
}
|
||||
Action::Restore => {
|
||||
selected_packages.drain_filter(|i| self.phone_packages[*i].state == PackageState::Enabled);
|
||||
}
|
||||
}
|
||||
|
||||
for i in selected_packages {
|
||||
let success = action_handler(
|
||||
&self.phone_packages[i],
|
||||
phone,
|
||||
|
|
@ -168,9 +216,10 @@ impl List {
|
|||
).unwrap_or_else(|err| err);
|
||||
|
||||
if success {
|
||||
update_selection_count(&mut self.selection, self.phone_packages[i].state, false);
|
||||
self.phone_packages[i].state = self.phone_packages[i].state.opposite(settings.disable_mode);
|
||||
self.phone_packages[i].selected = false;
|
||||
self.selected_packages.drain_filter(|s_i| *s_i == i);
|
||||
self.selection.selected_packages.drain_filter(|s_i| *s_i == i);
|
||||
}
|
||||
}
|
||||
Self::filter_package_lists(self);
|
||||
|
|
@ -179,12 +228,23 @@ impl List {
|
|||
Message::SelectAllPressed => {
|
||||
for i in self.filtered_packages.clone() {
|
||||
self.phone_packages[i].selected = true;
|
||||
if !self.selected_packages.contains(&i) {
|
||||
self.selected_packages.push(i);
|
||||
if !self.selection.selected_packages.contains(&i) {
|
||||
self.selection.selected_packages.push(i);
|
||||
update_selection_count(&mut self.selection, self.phone_packages[i].state, true);
|
||||
}
|
||||
}
|
||||
Command::none()
|
||||
},
|
||||
Message::ExportSelectionPressed => {
|
||||
Command::perform(export_selection(self.phone_packages.clone()), Message::ExportedSelection)
|
||||
}
|
||||
Message::ExportedSelection(export) => {
|
||||
match export {
|
||||
Ok(_) => info!("Selection exported"),
|
||||
Err(err) => error!("Selection export: {}", err),
|
||||
};
|
||||
Command::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -269,43 +329,55 @@ impl List {
|
|||
.width(Length::Fill);
|
||||
|
||||
|
||||
let action = if settings.disable_mode {"Enable/Disable"} else {"Uninstall/Restore"};
|
||||
let apply_selection_btn = Button::new(
|
||||
&mut self.apply_selection_btn_state,
|
||||
Row::new()
|
||||
.align_items(Alignment::Center)
|
||||
.push(Text::new(format!("{} selection ({})", action, self.selected_packages.len())))
|
||||
let restore_action = if settings.disable_mode {"Enable/Restore"} else {"Restore"};
|
||||
let remove_action = if settings.disable_mode {"Disable"} else {"Uninstall"};
|
||||
|
||||
let apply_restore_selection = Button::new(
|
||||
&mut self.apply_restore_selection,
|
||||
Text::new(format!("{} selection ({})",
|
||||
restore_action,
|
||||
self.selection.uninstalled + self.selection.disabled)
|
||||
)
|
||||
)
|
||||
.on_press(Message::ApplyActionOnSelection)
|
||||
.on_press(Message::ApplyActionOnSelection(Action::Restore))
|
||||
.padding(5)
|
||||
.height(Length::Units(40))
|
||||
.style(style::PrimaryButton::Enabled);
|
||||
|
||||
let select_all_btn = Button::new(
|
||||
&mut self.select_all_btn_state,
|
||||
Row::new()
|
||||
.align_items(Alignment::Center)
|
||||
.push(Text::new("Select all"))
|
||||
)
|
||||
.on_press(Message::SelectAllPressed)
|
||||
let apply_remove_selection = Button::new(
|
||||
&mut self.apply_remove_selection,
|
||||
Text::new(format!("{} selection ({})", remove_action, self.selection.enabled)))
|
||||
.on_press(Message::ApplyActionOnSelection(Action::Remove))
|
||||
.padding(5)
|
||||
.height(Length::Units(40))
|
||||
.style(style::PrimaryButton::Enabled);
|
||||
|
||||
let select_all_btn = Button::new(&mut self.select_all_btn_state, Text::new("Select all"))
|
||||
.padding(5)
|
||||
.on_press(Message::SelectAllPressed)
|
||||
.style(style::PrimaryButton::Enabled);
|
||||
|
||||
let export_selection_btn = Button::new(
|
||||
&mut self.export_selection_btn_state,
|
||||
Text::new(format!("Export current selection ({})", self.selection.selected_packages.len())))
|
||||
.padding(5)
|
||||
.on_press(Message::ExportSelectionPressed)
|
||||
.style(style::PrimaryButton::Enabled);
|
||||
|
||||
let action_row = Row::new()
|
||||
.width(Length::Fill)
|
||||
.spacing(10)
|
||||
.align_items(Alignment::Center)
|
||||
.height(Length::FillPortion(1))
|
||||
.push(select_all_btn)
|
||||
.push(Space::new(Length::Fill, Length::Shrink))
|
||||
.push(apply_selection_btn);
|
||||
.push(export_selection_btn)
|
||||
.push(apply_restore_selection)
|
||||
.push(apply_remove_selection);
|
||||
|
||||
let content = Column::new()
|
||||
.width(Length::Fill)
|
||||
.spacing(10)
|
||||
.align_items(Alignment::Center)
|
||||
.push(control_panel)
|
||||
.push(Space::new(Length::Fill, Length::Units(2)))
|
||||
.push(Space::new(Length::Fill, Length::Shrink))
|
||||
.push(packages_scrollable)
|
||||
.push(description_panel)
|
||||
.push(action_row);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue