mirror of
https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation.git
synced 2026-08-26 07:24:19 +02:00
Asynchronous + multi-threaded ADB commands (closes #65) 🚀
Smarter use of iced::Command to run as many tasks as possible concurrently/in parallel - ADB commands no longer block the interaction with the UI. - The UI now reacts in real-time to ADB actions (no longer waits for all actions to finish) - Some refactoring along the way
This commit is contained in:
parent
c93e722d27
commit
608c05a408
4 changed files with 158 additions and 156 deletions
|
|
@ -12,14 +12,18 @@ and `Removed`.
|
|||
|
||||
### Added
|
||||
- [[#52](https://github.com/0x192/universal-android-debloater/issues/52)] `uk.co.ee.myee` to the debloat lists (thanks [@lawson58](https://github.com/lawson85)).
|
||||
- [[#58](https://github.com/0x192/universal-android-debloater/issues/52)] `android` to the debloat lists with the tag `Unsafe`.
|
||||
- [[#49](https://github.com/0x192/universal-android-debloater/issues/49)] Multi-device support: You are now able to select a device among the list of all ADB connected devices/emulators.
|
||||
- [[#44](https://github.com/0x192/universal-android-debloater/issues/44)] Persistent settings: Settings (only `theme` for now) are saved to a config file. Its location follows [the standards of the different platforms](https://github.com/dirs-dev/dirs-rs#example).
|
||||
|
||||
### Changed
|
||||
- Review of the package lists recommendations. The `Recommended` debloat list is now safer (less likely to remove something you'd want to keep).
|
||||
- [[#65](https://github.com/0x192/universal-android-debloater/issues/65)] ADB commands now run in parallel and asynchronously! This means no more UI freeze when performing long/many actions! :rocket:
|
||||
- UI now updates itself in real time when performing ADB actions (thanks to async & multithreading). Before, it waited for the end of all actions.
|
||||
|
||||
### Fixed
|
||||
- Miscellaneous minor issues in some package descriptions.
|
||||
- Several bad recommendations.
|
||||
- [[#50](https://github.com/0x192/universal-android-debloater/issues/50)] Resync button flipping theme back to `Lupin`.
|
||||
|
||||
## [0.3] - 2021-10-10
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ use std::process::Command;
|
|||
#[cfg(target_os = "windows")]
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
#[dynamic]
|
||||
static RE: Regex = Regex::new(r"\n([[:ascii:]]+)\s+device").unwrap();
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Phone {
|
||||
pub model: String,
|
||||
|
|
@ -121,15 +124,24 @@ pub fn action_handler(
|
|||
package: &PackageRow,
|
||||
phone: &Phone,
|
||||
settings: &SettingsPhone,
|
||||
) -> Result<bool, bool> {
|
||||
let actions: Vec<String> = match package.state {
|
||||
) -> Vec<String> {
|
||||
// https://github.com/0x192/universal-android-debloater/wiki/ADB-reference
|
||||
match package.state {
|
||||
PackageState::Enabled => match settings.disable_mode {
|
||||
true => {
|
||||
if phone.android_sdk < 21 {
|
||||
// < Android Ice Cream Sandwich (4.0)
|
||||
if phone.android_sdk < 14 {
|
||||
vec![
|
||||
format!("pm disable {}", package.name),
|
||||
format!("pm clear {}", package.name),
|
||||
]
|
||||
}
|
||||
// < Android Lollipop (5.0)
|
||||
else if phone.android_sdk < 17 {
|
||||
vec![
|
||||
format!("am force-stop {}", package.name),
|
||||
format!("pm disable-user {}", package.name),
|
||||
format!("pm clear {}", package.name),
|
||||
format!("pm hide {}", package.name),
|
||||
]
|
||||
} else if settings.multi_user_mode {
|
||||
phone
|
||||
|
|
@ -137,15 +149,15 @@ pub fn action_handler(
|
|||
.iter()
|
||||
.flat_map(|u| {
|
||||
[
|
||||
format!("am force-stop --user {} {}", u.id, package.name),
|
||||
format!("pm disable-user --user {} {}", u.id, package.name),
|
||||
format!("am force-stop --user {} {}", u.id, package.name),
|
||||
format!("pm clear --user {} {}", u.id, package.name),
|
||||
//format!("pm hide --user {} {}", u.id, package.name),
|
||||
]
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![
|
||||
format!("am force-stop --user {} {}", selected_user.id, package.name),
|
||||
format!(
|
||||
"pm disable-user --user {} {}",
|
||||
selected_user.id, package.name
|
||||
|
|
@ -209,23 +221,7 @@ pub fn action_handler(
|
|||
}
|
||||
}
|
||||
PackageState::All => vec![], // This can't happen (like... never)
|
||||
};
|
||||
|
||||
for action in actions {
|
||||
match adb_shell_command(true, &action) {
|
||||
Ok(_) => {
|
||||
info!("[{}] {}", package.removal, action);
|
||||
}
|
||||
Err(err) => {
|
||||
if err.contains("[not installed for") {
|
||||
} else {
|
||||
error!("[{}] {} -> {}", package.removal, action, err);
|
||||
return Err(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn get_phone_model() -> String {
|
||||
|
|
@ -276,15 +272,12 @@ pub fn get_user_list() -> Vec<User> {
|
|||
}
|
||||
|
||||
// getprop ro.serialno
|
||||
pub fn get_device_list() -> Vec<Phone> {
|
||||
#[dynamic]
|
||||
static RE: Regex = Regex::new(r"\n([[:ascii:]]+)\s+device").unwrap();
|
||||
|
||||
pub async fn get_device_list() -> Vec<Phone> {
|
||||
match adb_shell_command(false, "devices") {
|
||||
Ok(devices) => {
|
||||
let mut device_list: Vec<Phone> = vec![];
|
||||
for device in RE.captures_iter(&devices) {
|
||||
env::set_var("ANDROID_SERIAL", device[1].to_string());
|
||||
env::set_var("ANDROID_SERIAL", &device[1]);
|
||||
|
||||
device_list.push(Phone {
|
||||
model: get_phone_brand(),
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@ pub enum Message {
|
|||
AppsAction(AppsMessage),
|
||||
SettingsAction(SettingsMessage),
|
||||
RefreshButtonPressed,
|
||||
Init,
|
||||
LoadDeviceList(Vec<Phone>),
|
||||
Init(Vec<Phone>),
|
||||
}
|
||||
|
||||
impl Application for UadGui {
|
||||
|
|
@ -73,21 +74,24 @@ impl Application for UadGui {
|
|||
fn new(_flags: ()) -> (Self, Command<Message>) {
|
||||
(
|
||||
Self::default(),
|
||||
Command::perform(Self::init(), |_| Message::Init),
|
||||
Command::perform(get_device_list(), Message::Init),
|
||||
)
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("UadGui")
|
||||
String::from("Universal Android Debloater")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Init => {
|
||||
Message::Init(device_list) => {
|
||||
self.device_list = device_list;
|
||||
self.selected_device = Some(Phone::default());
|
||||
Command::perform(Self::refresh(10), Message::LoadDevices)
|
||||
}
|
||||
Message::RefreshButtonPressed => {
|
||||
Message::LoadDeviceList(device_list) => {
|
||||
self.device_list = device_list;
|
||||
|
||||
// Save the current selected device
|
||||
let i = self
|
||||
.device_list
|
||||
|
|
@ -97,9 +101,15 @@ impl Application for UadGui {
|
|||
self.selected_device = Some(Phone::default());
|
||||
Command::perform(Self::refresh(i), Message::LoadDevices)
|
||||
}
|
||||
Message::RefreshButtonPressed => {
|
||||
self.apps_view.ready = false;
|
||||
Command::batch([
|
||||
Command::perform(Self::please_wait(), Message::AppsAction),
|
||||
Command::perform(Self::device_lists(), Message::LoadDeviceList),
|
||||
])
|
||||
}
|
||||
Message::LoadDevices(last_selected_device) => {
|
||||
self.settings_view.phone = SettingsPhone::default();
|
||||
self.device_list = get_device_list();
|
||||
|
||||
// Try to reload last selected phone
|
||||
if !self.device_list.is_empty() {
|
||||
|
|
@ -110,19 +120,12 @@ impl Application for UadGui {
|
|||
None => Some(Phone::default()),
|
||||
},
|
||||
};
|
||||
env::set_var(
|
||||
"ANDROID_SERIAL",
|
||||
self.selected_device.clone().unwrap().adb_id,
|
||||
);
|
||||
info!("{:-^65}", "-");
|
||||
info!(
|
||||
"ANDROID_SDK: {} | PHONE: {}",
|
||||
self.selected_device.as_ref().unwrap().android_sdk,
|
||||
self.selected_device.as_ref().unwrap().model
|
||||
);
|
||||
self.apps_view = AppsView::default();
|
||||
self.view = View::List;
|
||||
Command::perform(Self::load_phone_packages(), Message::AppsAction)
|
||||
Command::perform(
|
||||
Self::load_phone_packages(self.selected_device.clone().unwrap()),
|
||||
Message::AppsAction,
|
||||
)
|
||||
} else {
|
||||
self.selected_device = None;
|
||||
Command::none()
|
||||
|
|
@ -154,19 +157,12 @@ impl Application for UadGui {
|
|||
}
|
||||
Message::DeviceSelected(device) => {
|
||||
self.selected_device = Some(device);
|
||||
env::set_var(
|
||||
"ANDROID_SERIAL",
|
||||
self.selected_device.clone().unwrap().adb_id,
|
||||
);
|
||||
info!("{:-^65}", "-");
|
||||
info!(
|
||||
"ANDROID_SDK: {} | PHONE: {}",
|
||||
self.selected_device.as_ref().unwrap().android_sdk,
|
||||
self.selected_device.as_ref().unwrap().model
|
||||
);
|
||||
self.apps_view = AppsView::default();
|
||||
self.view = View::List;
|
||||
Command::perform(Self::load_phone_packages(), Message::AppsAction)
|
||||
Command::perform(
|
||||
Self::load_phone_packages(self.selected_device.clone().unwrap()),
|
||||
Message::AppsAction,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -267,17 +263,25 @@ impl UadGui {
|
|||
Self::run(settings).unwrap_err();
|
||||
}
|
||||
|
||||
pub async fn load_phone_packages() -> AppsMessage {
|
||||
pub async fn load_phone_packages(phone: Phone) -> AppsMessage {
|
||||
env::set_var("ANDROID_SERIAL", phone.adb_id);
|
||||
info!("{:-^65}", "-");
|
||||
info!(
|
||||
"ANDROID_SDK: {} | PHONE: {}",
|
||||
phone.android_sdk, phone.model
|
||||
);
|
||||
AppsMessage::LoadPackages
|
||||
}
|
||||
|
||||
pub async fn init() -> Message {
|
||||
Message::Init
|
||||
}
|
||||
|
||||
pub async fn refresh(i: usize) -> usize {
|
||||
i
|
||||
}
|
||||
pub async fn device_lists() -> Vec<Phone> {
|
||||
get_device_list().await
|
||||
}
|
||||
pub async fn please_wait() -> AppsMessage {
|
||||
AppsMessage::Nothing
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh_icon() -> Text {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::core::sync::{action_handler, Phone, User};
|
||||
use crate::core::sync::{action_handler, adb_shell_command, Phone, User};
|
||||
use crate::core::uad_lists::{
|
||||
load_debloat_lists, Opposite, Package, PackageState, Removal, UadList,
|
||||
};
|
||||
|
|
@ -51,7 +51,7 @@ pub enum Action {
|
|||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct List {
|
||||
ready: bool,
|
||||
pub ready: bool,
|
||||
phone_packages: Vec<Vec<PackageRow>>, // packages of all users of the phone
|
||||
filtered_packages: Vec<usize>, // phone_packages indexes of the selected user (= what you see on screen)
|
||||
pub selection: Selection,
|
||||
|
|
@ -87,6 +87,9 @@ pub enum Message {
|
|||
ExportSelectionPressed,
|
||||
List(usize, RowMessage),
|
||||
ExportedSelection(Result<bool, String>),
|
||||
ActionIsDone(usize),
|
||||
PackagesLoaded(Vec<Vec<PackageRow>>),
|
||||
Nothing,
|
||||
}
|
||||
|
||||
impl List {
|
||||
|
|
@ -98,21 +101,19 @@ impl List {
|
|||
) -> Command<Message> {
|
||||
let i_user = &self.selected_user.unwrap_or(User { id: 0, index: 0 }).index; // for readability
|
||||
match message {
|
||||
Message::Nothing => Command::none(),
|
||||
Message::LoadPackages => {
|
||||
self.selected_package_state = Some(PackageState::Enabled);
|
||||
self.selected_list = Some(UadList::All);
|
||||
self.selected_removal = Some(Removal::Recommended);
|
||||
self.selected_user = Some(User { id: 0, index: 0 });
|
||||
|
||||
match phone.user_list.len() {
|
||||
0 | 1 => self.phone_packages.push(fetch_packages(&UAD_LISTS, &None)),
|
||||
_ => {
|
||||
for user in &phone.user_list {
|
||||
self.phone_packages
|
||||
.push(fetch_packages(&UAD_LISTS, &Some(user)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Command::perform(
|
||||
Self::load_packages(phone.user_list.clone()),
|
||||
Message::PackagesLoaded,
|
||||
)
|
||||
}
|
||||
Message::PackagesLoaded(packages) => {
|
||||
self.phone_packages = packages;
|
||||
self.filtered_packages = (0..self.phone_packages[*i_user].len()).collect();
|
||||
Self::filter_package_lists(self);
|
||||
|
||||
|
|
@ -148,79 +149,48 @@ impl List {
|
|||
.update(row_message.clone())
|
||||
.map(move |row_message| Message::List(i, row_message));
|
||||
|
||||
let package = &mut self.phone_packages[*i_user][i];
|
||||
|
||||
match row_message {
|
||||
RowMessage::ToggleSelection(toggle) => {
|
||||
if self.phone_packages[*i_user][i].removal == Removal::Unsafe
|
||||
&& !settings.expert_mode
|
||||
{
|
||||
self.phone_packages[*i_user][i].selected = false;
|
||||
if package.removal == Removal::Unsafe && !settings.expert_mode {
|
||||
package.selected = false;
|
||||
} else {
|
||||
self.phone_packages[*i_user][i].selected = toggle;
|
||||
package.selected = toggle;
|
||||
|
||||
if self.phone_packages[*i_user][i].selected {
|
||||
if package.selected {
|
||||
self.selection.selected_packages.push(i);
|
||||
update_selection_count(
|
||||
&mut self.selection,
|
||||
self.phone_packages[*i_user][i].state,
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
self.selection
|
||||
.selected_packages
|
||||
.drain_filter(|s_i| *s_i == i);
|
||||
update_selection_count(
|
||||
&mut self.selection,
|
||||
self.phone_packages[*i_user][i].state,
|
||||
false,
|
||||
);
|
||||
}
|
||||
update_selection_count(
|
||||
&mut self.selection,
|
||||
package.state,
|
||||
package.selected,
|
||||
);
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
RowMessage::ActionPressed => {
|
||||
let success = action_handler(
|
||||
&self.selected_user.unwrap(),
|
||||
&self.phone_packages[*i_user][i],
|
||||
phone,
|
||||
settings,
|
||||
)
|
||||
.unwrap_or_else(|err| err);
|
||||
let mut commands = vec![];
|
||||
let actions =
|
||||
action_handler(&self.selected_user.unwrap(), package, phone, settings);
|
||||
|
||||
if success {
|
||||
let i = self.phone_packages[*i_user]
|
||||
.iter()
|
||||
.position(|p| p.name == self.phone_packages[*i_user][i].name)
|
||||
.unwrap();
|
||||
if self.phone_packages[*i_user][i].selected {
|
||||
update_selection_count(
|
||||
&mut self.selection,
|
||||
self.phone_packages[*i_user][i].state,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
self.phone_packages[*i_user][i].state = self.phone_packages[*i_user][i]
|
||||
.state
|
||||
.opposite(settings.disable_mode);
|
||||
|
||||
if settings.multi_user_mode {
|
||||
for u in &phone.user_list {
|
||||
self.phone_packages[u.index][i].state =
|
||||
self.phone_packages[*i_user][i].state;
|
||||
self.phone_packages[u.index][i].selected = false;
|
||||
}
|
||||
}
|
||||
self.selection
|
||||
.selected_packages
|
||||
.drain_filter(|s_i| *s_i == i);
|
||||
self.phone_packages[*i_user][i].selected = false;
|
||||
Self::filter_package_lists(self);
|
||||
for action in actions {
|
||||
commands.push(Command::perform(
|
||||
Self::perform_commands(action, i, package.removal),
|
||||
Message::ActionIsDone,
|
||||
));
|
||||
}
|
||||
Command::batch(commands)
|
||||
}
|
||||
RowMessage::PackagePressed => {
|
||||
self.description = self.phone_packages[*i_user][i].clone().description;
|
||||
self.description = package.clone().description;
|
||||
Command::none()
|
||||
}
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
Message::ApplyActionOnSelection(action) => {
|
||||
let mut selected_packages = self.selection.selected_packages.clone();
|
||||
|
|
@ -237,42 +207,26 @@ impl List {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut commands = vec![];
|
||||
for i in selected_packages {
|
||||
let success = action_handler(
|
||||
let actions = action_handler(
|
||||
&self.selected_user.unwrap(),
|
||||
&self.phone_packages[*i_user][i],
|
||||
phone,
|
||||
settings,
|
||||
)
|
||||
.unwrap_or_else(|err| err);
|
||||
|
||||
if success {
|
||||
update_selection_count(
|
||||
&mut self.selection,
|
||||
self.phone_packages[*i_user][i].state,
|
||||
false,
|
||||
);
|
||||
if !settings.multi_user_mode {
|
||||
self.phone_packages[*i_user][i].state = self.phone_packages[*i_user][i]
|
||||
.state
|
||||
.opposite(settings.disable_mode);
|
||||
self.phone_packages[*i_user][i].selected = false;
|
||||
}
|
||||
|
||||
for u in &phone.user_list {
|
||||
self.phone_packages[u.index][i].state = self.phone_packages[u.index][i]
|
||||
.state
|
||||
.opposite(settings.disable_mode);
|
||||
self.phone_packages[u.index][i].selected = false;
|
||||
}
|
||||
self.selection
|
||||
.selected_packages
|
||||
.drain_filter(|s_i| *s_i == i);
|
||||
);
|
||||
for action in actions {
|
||||
commands.push(Command::perform(
|
||||
Self::perform_commands(
|
||||
action,
|
||||
i,
|
||||
self.phone_packages[*i_user][i].removal,
|
||||
),
|
||||
Message::ActionIsDone,
|
||||
));
|
||||
}
|
||||
}
|
||||
Self::filter_package_lists(self);
|
||||
Command::none()
|
||||
Command::batch(commands)
|
||||
}
|
||||
Message::SelectAllPressed => {
|
||||
for i in self.filtered_packages.clone() {
|
||||
|
|
@ -311,6 +265,28 @@ impl List {
|
|||
Self::filter_package_lists(self);
|
||||
Command::none()
|
||||
}
|
||||
Message::ActionIsDone(i) => {
|
||||
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);
|
||||
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);
|
||||
self.phone_packages[u.index][i].selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
self.selection
|
||||
.selected_packages
|
||||
.drain_filter(|s_i| *s_i == i);
|
||||
Self::filter_package_lists(self);
|
||||
Command::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -510,6 +486,31 @@ impl List {
|
|||
.map(|(i, _)| i)
|
||||
.collect();
|
||||
}
|
||||
|
||||
async fn perform_commands(action: String, i: usize, recommendation: Removal) -> usize {
|
||||
match adb_shell_command(true, &action) {
|
||||
Ok(_) => info!("[{}] {}", recommendation, action),
|
||||
Err(err) => {
|
||||
if !err.contains("[not installed for") {
|
||||
error!("[{}] {} -> {}", recommendation, action, err);
|
||||
}
|
||||
}
|
||||
};
|
||||
i
|
||||
}
|
||||
|
||||
async fn load_packages(user_list: Vec<User>) -> Vec<Vec<PackageRow>> {
|
||||
let mut temp = vec![];
|
||||
match user_list.len() {
|
||||
0 | 1 => temp.push(fetch_packages(&UAD_LISTS, &None)),
|
||||
_ => {
|
||||
for user in &user_list {
|
||||
temp.push(fetch_packages(&UAD_LISTS, &Some(user)));
|
||||
}
|
||||
}
|
||||
}
|
||||
temp
|
||||
}
|
||||
}
|
||||
|
||||
fn loading_data<'a>(settings: &Settings) -> Element<'a, Message> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue