refactor: apply extra clippy lints suggestions (#673)

Cherry picking of lints from clippy::pedantic / clippy::nursery / clippy::unwrap_used / clippy::expect_used

Notable changes :
- consideration of all clippy::use-self lints
- replaced sort() by unstable_sort()
- use of map_or_else() when appropriate 
- replaced "".to_string() by String::new()
- replaced collect() + len() by count()

The code base now passes all the clippy::nursery lints

---------
Co-authored-by: w1nst0n <w1nst0n@keemail.me>
This commit is contained in:
Amaan Qureshi 2023-04-10 05:33:12 -04:00 committed by GitHub
parent 69235a9aa5
commit 68092ff987
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 429 additions and 403 deletions

View file

@ -1,28 +1,34 @@
--- ---
name: Add new package(s) name: Add new package(s)
about: You want to add new apps in the debloat list about: You want to add new apps in the debloat list
title: '' title: ""
labels: package::addition labels: package::addition
assignees: '' assignees: ""
--- ---
**Your phone:** <phone model> **Your phone model:**
**Packages:** **Packages:**
``` ```
com.this.is.a.bad.application com.this.is.a.bad.application
com.this.is.another.bad.application com.this.is.another.bad.application
... ...
``` ```
- [ ] **I removed all those packages on my phone** - [ ] **I removed all those packages on my phone**
If not why. Leave the brackets blank and explain why. If not why. Leave the brackets blank and explain why.
## Document each package the best you can ## Document each package the best you can
**List**: `Google`|`Misc`|`Oem` (manufacturer)|`Aosp`|`Pending`|`Carrier` (isp).
**Removal**: `Recommended`, `Advanced`, `Expert` (this can break important features), `Unsafe` (this can bootloop the phone or break extremely important features). **List**: `Google`|`Misc`|`OEM` (manufacturer)|`AOSP`|`Pending`|`Carrier` (isp).
**Removal**: `Recommended`, `Advanced`, `Expert` (this can break important features),
or `Unsafe` (this can bootloop the phone or break extremely important features).
### \<package name\> ### \<package name\>
**List**: \<list\>
**List**: \<list\>
**Removal**: \<recommendation list\> **Removal**: \<recommendation list\>
> Description. Link to its Playstore page if it exists. > Description. Link to its Playstore page if it exists.

View file

@ -1,10 +1,9 @@
--- ---
name: Bug report name: Bug report
about: You have an issue with the UAD software itself about: You have an issue with the UAD software itself
title: '' title: ""
labels: bug labels: bug
assignees: '' assignees: ""
--- ---
**Describe the bug** **Describe the bug**

View file

@ -1,13 +1,12 @@
--- ---
name: Debloat issue report name: Debloat issue report
about: Your phone has unexpected issues after debloating about: Your phone has unexpected issues after debloating
title: '' title: ""
labels: package::breakage labels: package::breakage
assignees: '' assignees: ""
--- ---
**Your phone**: \<phone model\> **Your phone model**:
**Describe the issue** **Describe the issue**
A clear and concise description of what the problem is. A clear and concise description of what the problem is.
@ -16,4 +15,4 @@ A clear and concise description of what the problem is.
What to do to fix the issue. What to do to fix the issue.
**UAD log** **UAD log**
Upload the logfile generated by UAD. You can hardly be helped without. Upload the logfile generated by UAD. It would be difficult to help you without it.

View file

@ -1,10 +1,9 @@
--- ---
name: Feature request name: Feature request
about: You want a new feature about: You want a new feature
title: '' title: ""
labels: enhancement labels: enhancement
assignees: '' assignees: ""
--- ---
**Describe the feature you want** **Describe the feature you want**

View file

@ -1,30 +1,37 @@
--- ---
name: Update apps description or recommendation name: Update apps description or recommendation
about: You want to improve/update a description/recommendation about: You want to improve/update a description/recommendation
title: '' title: ""
labels: package::documentation labels: package::documentation
assignees: '' assignees: ""
--- ---
**Your phone**: \<phone model\> **Your phone model**:
**Packages documentation to update:** **Packages documentation to update:**
``` ```
com.this.is.a.application com.this.is.a.application
com.this.is.another.application com.this.is.another.application
... ...
``` ```
## Documentation chage
**List**: `Google`|`Misc`|`Oem` (manufacturer)|`Aosp`|`Pending`|`Carrier` (isp). ## Documentation Change
**Removal**: `Recommended`, `Advanced`, `Expert` (this can break important features), `Unsafe` (this can bootloop the phone or break extremely important features).
**List**: `Google`|`Misc`|`OEM` (manufacturer)|`AOSP`|`Pending`|`Carrier` (isp).
**Removal**: `Recommended`, `Advanced`, `Expert` (this can break important features),
or `Unsafe` (this can bootloop the phone or break extremely important features).
### \<package name\> ### \<package name\>
**List**: \<current list\> :arrow_right: \<proposed list\>
**Removal**: \<current recommendation list\> :arrow_right: \<proposed recommendation list\>
**Current description** **List**: \<current list\> :arrow_right: \<proposed list\>
**Removal**: \<current recommendation list\>
:arrow_right: \<proposed recommendation list\>
### Current description
> Current description > Current description
**Proposed description** ### Proposed description
> Proposed description > Proposed description

View file

@ -25,9 +25,9 @@ jobs:
- graphics: glow - graphics: glow
renderer: "-opengl" renderer: "-opengl"
- graphics: wgpu - graphics: wgpu
renderer: '' # Vulkan but we don't want this in the binary filename renderer: "" # Vulkan but we don't want this in the binary filename
- update_feature: self-update - update_feature: self-update
update_name: '' # we don't want this in the binary filename update_name: "" # we don't want this in the binary filename
- update_feature: no-self-update - update_feature: no-self-update
update_name: "-noseflupdate" update_name: "-noseflupdate"
steps: steps:
@ -62,4 +62,4 @@ jobs:
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: uad_gui${{ matrix.update_name }}-${{ matrix.build_target }}${{ matrix.renderer }} name: uad_gui${{ matrix.update_name }}-${{ matrix.build_target }}${{ matrix.renderer }}
path: bin/uad_gui-* path: bin/uad_gui-*

View file

@ -2,16 +2,16 @@ name: Continuous Integration
on: on:
push: push:
paths: paths:
- '**.rs' - "**.rs"
- 'Cargo.lock' - "Cargo.lock"
- 'Cargo.toml' - "Cargo.toml"
- '**.json' - "**.json"
pull_request: pull_request:
paths: paths:
- '**.rs' - "**.rs"
- 'Cargo.lock' - "Cargo.lock"
- 'Cargo.toml' - "Cargo.toml"
- '**.json' - "**.json"
jobs: jobs:
lint: lint:
@ -33,7 +33,7 @@ jobs:
- lint: check - lint: check
args: " --all-features" args: " --all-features"
- lint: test - lint: test
args: '' args: ""
- lint: clippy - lint: clippy
args: " --all --all-features -- -D warnings" args: " --all --all-features -- -D warnings"
- lint: fmt - lint: fmt

View file

@ -5,9 +5,9 @@ on:
branches: branches:
- main - main
paths: paths:
- '**.rs' - "**.rs"
- 'Cargo.lock' - "Cargo.lock"
- 'Cargo.toml' - "Cargo.toml"
tags-ignore: tags-ignore:
- dev-build - dev-build

2
.gitignore vendored
View file

@ -1,3 +1,3 @@
debug/ debug/
target/ target/
*.log *.log

View file

@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found. the "copyright" line and a pointer to where the full notice is found.
Universal Android Debloater GUI Universal Android Debloater GUI
Copyright (C) 2021 W1nst0n Copyright (C) 2021 W1nst0n
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode: notice like this when it starts in an interactive mode:
<program> Copyright (C) 2021 W1nst0n Universal Android Debloater GUI Copyright (C) 2021 W1nst0n
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details. under certain conditions; type `show c' for details.

176
README.md
View file

@ -1,97 +1,122 @@
# Universal Android Debloater GUI # Universal Android Debloater GUI
**DISCLAIMER**: Use at your own risk. I am not responsible for anything that could happen to your phone.
**DISCLAIMER**: Use at your own risk. I am not responsible for anything that
could happen to your phone.
<img src="/resources/screenshots/v0.5.0.png" width="850" alt="uad_screenshot"> <img src="/resources/screenshots/v0.5.0.png" width="850" alt="uad_screenshot">
**This software is still in an early stage of development. Check out the issues, and feel free to contribute!** **This software is still in an early stage of development. Check out the issues, and feel free to contribute!**
## Summary ## Summary
This is a complete rewrite in Rust of the [UAD project](https://gitlab.com/W1nst0n/universal-android-debloater), which aims to improve privacy and battery performance by removing unnecessary and obscure system apps. This can also contribute to improve security by reducing [the attack surface](https://en.wikipedia.org/wiki/Attack_surface).
Packages are as well documented as possible in order to provide a better understanding of what you can delete or not. The worst thing which could happen is removing an essential system package needed during boot causing then an unfortunate bootloop. After about 5 failed system boots, the phone will automatically reboot in recovery mode and you'll have to perform a FACTORY RESET. So make a backup first! This is a complete rewrite in Rust of the [UAD project](https://gitlab.com/W1nst0n/universal-android-debloater),
which aims to improve privacy and battery performance by removing unnecessary
and obscure system apps.
This can also contribute to improve security by reducing [the attack surface](https://en.wikipedia.org/wiki/Attack_surface).
In any case, you can NOT brick your device with this software! That's the main thing, right? Packages are as well documented as possible in order to provide a better
understanding of what you can delete or not. The worst issue that could happen
is removing an essential system package needed during boot causing then an unfortunate
bootloop. After about 5 failed system boots, the phone will automatically reboot
in recovery mode, and you'll have to perform a FACTORY RESET. Make a backup first!
In any case, you **CANNOT** brick your device with this software!
That's the main point, right?
## Features ## Features
* [X] Uninstall/Disable and Restore/Enable system packages
* [X] Multi-user support (e.g apps in work profiles)
* [X] Export/Import your selection in `uad_exported_selection.txt`
* [X] Multi-device support: you can connect multiple phones at the same time
* [X] All your actions are logged so you never forget what you've done
NB : System apps cannot really be uninstalled without root (see the [FAQ](https://github.com/0x192/universal-android-debloater/wiki/FAQ)) - [x] Uninstall/Disable and Restore/Enable system packages
- [x] Multi-user support (e.g. apps in work profiles)
- [x] Export/Import your selection in `uad_exported_selection.txt`
- [x] Multi-device support: you can connect multiple phones at the same time
- [x] All your actions are logged, so you never forget what you've done
## Universal debloat lists NB : System apps cannot truly be uninstalled without root (see the [FAQ](https://github.com/0x192/universal-android-debloater/wiki/FAQ))
* [X] GFAM (Google/Facebook/Amazon/Microsoft)
* [X] AOSP ## Universal Debloat Lists
* [X] Manufacturers (OEM)
* [X] Mobile carriers - [x] GFAM (Google/Facebook/Amazon/Microsoft)
* [X] Qualcomm / Mediatek / Miscellaneous - [x] AOSP
- [x] Manufacturers (OEM)
- [x] Mobile carriers
- [x] Qualcomm / Mediatek / Miscellaneous
## Manufacturers debloat lists ## Manufacturers debloat lists
* [ ] Archos
* [X] Asus - [ ] Archos
* [ ] Blackberry - [x] Asus
* [ ] Gionee - [ ] Blackberry
* [X] LG - [ ] Gionee
* [X] Google - [x] LG
* [ ] iQOO - [x] Google
* [X] Fairphone - [ ] iQOO
* [ ] HTC - [x] Fairphone
* [X] Huawei - [ ] HTC
* [X] Motorola - [x] Huawei
* [X] Nokia - [x] Motorola
* [X] OnePlus - [x] Nokia
* [X] Oppo - [x] OnePlus
* [X] Realme - [x] Oppo
* [X] Samsung - [x] Realme
* [X] Sony - [x] Samsung
* [X] Tecno - [x] Sony
* [ ] TCL - [x] Tecno
* [X] Unihertz - [ ] TCL
* [X] Vivo/iQOO - [x] Unihertz
* [ ] Wiko - [x] Vivo/iQOO
* [X] Xiaomi - [ ] Wiko
* [X] ZTE - [x] Xiaomi
- [x] ZTE
## Mobile carriers debloat lists ## Mobile carriers debloat lists
| Country | Carriers |
|-----------------|-----------------------------------|
| France | Orange, SFR, Free, Bouygues |
| USA | T-Mobile, Verizon, Sprint, AT&T |
| Germany | Telekom |
| UK | EE |
| Country | Carriers |
| ------- | ------------------------------- |
| France | Orange, SFR, Free, Bouygues |
| USA | T-Mobile, Verizon, Sprint, AT&T |
| Germany | Telekom |
| UK | EE |
## How to use it ## How to use it
- **Read the [FAQ](https://github.com/0x192/universal-android-debloater/wiki/FAQ)!** - **Read the [FAQ](https://github.com/0x192/universal-android-debloater/wiki/FAQ)!**
- **Do a proper backup of your data! You can never be too careful!** - **Do a proper backup of your data! You can never be too careful!**
- Enable *Developer Options* on your smartphone. - Enable _Developer Options_ on your smartphone.
- Turn on *USB Debugging* from the developer panel. - Turn on _USB Debugging_ from the developer panel.
- From the settings, disconnect from any OEM accounts (when you delete an OEM account package it could lock you on the lockscreen because the phone can't associate your identity anymore) - From the settings, disconnect from any OEM accounts (when you delete an OEM
account package it could lock you on the lockscreen because the phone can't
associate your identity anymore)
- Install ADB (see the intructions by clicking on your OS below): - Install ADB (see the intructions by clicking on your OS below):
<p> <p>
<details> <details>
<summary>LINUX</summary> <summary>LINUX</summary>
- Install *Android platform tools* on your PC : - Install _Android platform tools_ on your PC :
Debian Base: Debian Base:
```bash ```bash
$ sudo apt install android-sdk-platform-tools sudo apt install android-sdk-platform-tools
``` ```
Arch-Linux Base: Arch-Linux Base:
```bash ```bash
$ sudo pacman -S android-tools sudo pacman -S android-tools
``` ```
Red Hat Base: Red Hat Base:
```bash ```bash
$ sudo yum install android-tools sudo yum install android-tools
``` ```
OpenSUSE Base: OpenSUSE Base:
```bash ```bash
$ sudo zypper install android-tools sudo zypper install android-tools
``` ```
</details> </details>
</p> </p>
@ -100,42 +125,55 @@ NB : System apps cannot really be uninstalled without root (see the [FAQ](https:
<summary>MAC OS</summary> <summary>MAC OS</summary>
- Install [Homebrew](https://brew.sh/) - Install [Homebrew](https://brew.sh/)
- Install *Android platform tools* - Install _Android platform tools_
```bash ```bash
$ brew install android-platform-tools brew install android-platform-tools
``` ```
</details>
</p> </details>
</p>
<p> <p>
<details> <details>
<summary>WINDOWS</summary> <summary>WINDOWS</summary>
- Download [android platform tools](https://dl.google.com/android/repository/platform-tools-latest-windows.zip) and unzip it somewhere. - Download [android platform tools](https://dl.google.com/android/repository/platform-tools-latest-windows.zip)
- [Add the android platform tools to your PATH](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/) **OR** make sure to launch UAD from the same directory. and unzip it somewhere.
- [Add the android platform tools to your PATH](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/)
**OR** make sure to launch UAD from the same directory.
- [Install USB drivers for your device](https://developer.android.com/studio/run/oem-usb#Drivers) - [Install USB drivers for your device](https://developer.android.com/studio/run/oem-usb#Drivers)
- Check your device is detected: - Check your device is detected:
```batch
> adb devices ```bash
adb devices
``` ```
</details>
</p>
</details>
</p>
- Download the latest release of UAD GUI for your Operating System [here](https://github.com/0x192/universal-android-debloater/releases). Take the `opengl` version only if the default version (with a Vulkan backend) doesn't launch. - Download the latest release of UAD GUI for your Operating System [here](https://github.com/0x192/universal-android-debloater/releases).
Take the `opengl` version only if the default version (with a Vulkan backend)
doesn't launch.
**NOTE:** Chinese phones users may need to use the AOSP list for removing some stock apps because those Chinese manufacturers (especially Xiaomi and Huawei) have been using the name of AOSP packages for their own (modified & closed-source) apps. **NOTE:** Chinese phones users may need to use the AOSP list for removing some stock
apps because those Chinese manufacturers (especially Xiaomi and Huawei) have been
using the name of AOSP packages for their own (modified & closed-source) apps.
**IMPORTANT NOTE:** You will have to run this software whenever your OEM pushes an update to your phone as some *uninstalled* system apps could be reinstalled. **IMPORTANT NOTE:** You will have to run this software whenever your OEM pushes
an update to your phone as some _uninstalled_ system apps could be reinstalled.
## How to contribute ## How to contribute
Hey-hey-hey! Don't go away so fast! This is a community project. That means I need you! I'm sure you want to make this project better anyway. Hey-hey-hey! Don't go away so fast! This is a community project.
That means I need you! I'm sure you want to make this project better anyway.
==> [How to contribute](https://github.com/0x192/universal-android-debloater/wiki) ==> [How to contribute](https://github.com/0x192/universal-android-debloater/wiki)
## Special thanks ## Special thanks
- [@mawilms](https://github.com/mawilms) for his LotRO plugin manager ([Lembas](https://github.com/mawilms/lembas)) which helped me a lot to understand how to use the [Iced](https://github.com/hecrj/iced) GUI library. - [@mawilms](https://github.com/mawilms) for his LotRO plugin manager ([Lembas](https://github.com/mawilms/lembas))
which helped me a lot to understand how to use the [Iced](https://github.com/hecrj/iced)
GUI library.
- [@casperstorm](https://github.com/casperstorm) for the UI/UX inspiration. - [@casperstorm](https://github.com/casperstorm) for the UI/UX inspiration.

View file

@ -55,43 +55,38 @@ static CONFIG_FILE: PathBuf = CONFIG_DIR.join("config.toml");
impl Config { impl Config {
pub fn save_changes(settings: &Settings, device_id: &String) { pub fn save_changes(settings: &Settings, device_id: &String) {
let mut config = Self::load_configuration_file(); let mut config = Self::load_configuration_file();
match config if let Some(device) = config
.devices .devices
.iter_mut() .iter_mut()
.find(|x| x.device_id == *device_id) .find(|x| x.device_id == *device_id)
{ {
Some(device) => { *device = settings.device.clone();
*device = settings.device.clone(); } else {
config.general = settings.general.clone(); debug!("config: New device settings saved");
} config.devices.push(settings.device.clone());
None => {
debug!("config: New device settings saved");
config.devices.push(settings.device.clone());
config.general = settings.general.clone();
}
} }
config.general = settings.general.clone();
let toml = toml::to_string(&config).unwrap(); let toml = toml::to_string(&config).unwrap();
fs::write(&*CONFIG_FILE, toml).expect("Could not write config file to disk!"); fs::write(&*CONFIG_FILE, toml).expect("Could not write config file to disk!");
} }
#[allow(clippy::option_if_let_else)]
pub fn load_configuration_file() -> Self { pub fn load_configuration_file() -> Self {
match fs::read_to_string(&*CONFIG_FILE) { if let Ok(s) = fs::read_to_string(&*CONFIG_FILE) {
Ok(s) => match toml::from_str(&s) { match toml::from_str(&s) {
Ok(config) => config, Ok(config) => config,
Err(e) => { Err(e) => {
error!("Invalid config file: `{}`", e); error!("Invalid config file: `{}`", e);
error!("Restoring default config file"); error!("Restoring default config file");
let toml = toml::to_string(&Config::default()).unwrap(); let toml = toml::to_string(&Self::default()).unwrap();
fs::write(&*CONFIG_FILE, toml).expect("Could not write config file to disk!"); fs::write(&*CONFIG_FILE, toml).expect("Could not write config file to disk!");
Config::default() Self::default()
} }
},
Err(_) => {
let default_conf = toml::to_string(&Config::default()).unwrap();
fs::write(&*CONFIG_FILE, default_conf)
.expect("Could not write config file to disk!");
Config::default()
} }
} else {
let default_conf = toml::to_string(&Self::default()).unwrap();
fs::write(&*CONFIG_FILE, default_conf).expect("Could not write config file to disk!");
Self::default()
} }
} }
} }

View file

@ -44,7 +44,7 @@ pub async fn backup_phone(
user_backup.packages.push(CorePackage { user_backup.packages.push(CorePackage {
name: p.name.clone(), name: p.name.clone(),
state: p.state, state: p.state,
}) });
} }
backup.users.push(user_backup); backup.users.push(user_backup);
} }
@ -71,6 +71,7 @@ pub async fn backup_phone(
} }
pub fn list_available_backups(dir: &Path) -> Vec<DisplayablePath> { pub fn list_available_backups(dir: &Path) -> Vec<DisplayablePath> {
#[allow(clippy::option_if_let_else)]
match fs::read_dir(dir) { match fs::read_dir(dir) {
Ok(files) => files Ok(files) => files
.filter_map(|e| e.ok()) .filter_map(|e| e.ok())
@ -129,15 +130,14 @@ pub fn restore_backup(
let mut commands = vec![]; let mut commands = vec![];
for u in phone_backup.users { for u in phone_backup.users {
let mut _index = 0; let index = match selected_device.user_list.iter().find(|x| x.id == u.id) {
match selected_device.user_list.iter().find(|x| x.id == u.id) { Some(i) => i.index,
Some(i) => _index = i.index,
None => return Err(format!("user {} doesn't exist", u.id)), None => return Err(format!("user {} doesn't exist", u.id)),
}; };
for (i, backup_package) in u.packages.iter().enumerate() { for (i, backup_package) in u.packages.iter().enumerate() {
let package: CorePackage; let package: CorePackage;
match packages[_index] match packages[index]
.iter() .iter()
.find(|x| x.name == backup_package.name) .find(|x| x.name == backup_package.name)
{ {
@ -150,8 +150,8 @@ pub fn restore_backup(
} }
} }
let p_commands = apply_pkg_state_commands( let p_commands = apply_pkg_state_commands(
package, &package,
&backup_package.state, backup_package.state,
&settings &settings
.backup .backup
.selected_user .selected_user

View file

@ -29,7 +29,7 @@ impl Default for Phone {
model: "fetching devices...".to_string(), model: "fetching devices...".to_string(),
android_sdk: 0, android_sdk: 0,
user_list: vec![], user_list: vec![],
adb_id: "".to_string(), adb_id: String::new(),
} }
} }
} }
@ -54,9 +54,10 @@ impl std::fmt::Display for User {
} }
pub fn adb_shell_command(shell: bool, args: &str) -> Result<String, String> { pub fn adb_shell_command(shell: bool, args: &str) -> Result<String, String> {
let adb_command = match shell { let adb_command = if shell {
true => vec!["shell", args], vec!["shell", args]
false => vec![args], } else {
vec![args]
}; };
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
@ -77,7 +78,12 @@ pub fn adb_shell_command(shell: bool, args: &str) -> Result<String, String> {
Err("ADB was not found".to_string()) Err("ADB was not found".to_string())
} }
Ok(o) => { Ok(o) => {
if !o.status.success() { if o.status.success() {
Ok(String::from_utf8(o.stdout)
.map_err(|e| e.to_string())?
.trim_end()
.to_string())
} else {
let stdout = String::from_utf8(o.stdout) let stdout = String::from_utf8(o.stdout)
.map_err(|e| e.to_string())? .map_err(|e| e.to_string())?
.trim_end() .trim_end()
@ -90,11 +96,6 @@ pub fn adb_shell_command(shell: bool, args: &str) -> Result<String, String> {
// ADB does really weird things. Some errors are not redirected to stderr // ADB does really weird things. Some errors are not redirected to stderr
let err = if stdout.is_empty() { stderr } else { stdout }; let err = if stdout.is_empty() { stderr } else { stdout };
Err(err) Err(err)
} else {
Ok(String::from_utf8(o.stdout)
.map_err(|e| e.to_string())?
.trim_end()
.to_string())
} }
} }
} }
@ -139,26 +140,28 @@ pub async fn perform_adb_commands(
} }
pub fn list_all_system_packages(user_id: Option<&User>) -> String { pub fn list_all_system_packages(user_id: Option<&User>) -> String {
#[allow(clippy::option_if_let_else)]
let action = match user_id { let action = match user_id {
Some(user_id) => format!("pm list packages -s -u --user {}", user_id.id), Some(user_id) => format!("pm list packages -s -u --user {}", user_id.id),
None => "pm list packages -s -u".to_string(), None => "pm list packages -s -u".to_string(),
}; };
adb_shell_command(true, &action) adb_shell_command(true, &action)
.unwrap_or_else(|_| "".to_string()) .unwrap_or_else(|_| String::new())
.replace("package:", "") .replace("package:", "")
} }
pub fn hashset_system_packages(state: PackageState, user_id: Option<&User>) -> HashSet<String> { pub fn hashset_system_packages(state: PackageState, user_id: Option<&User>) -> HashSet<String> {
#[allow(clippy::option_if_let_else)]
let user = match user_id { let user = match user_id {
Some(user_id) => format!(" --user {}", user_id.id), Some(user_id) => format!(" --user {}", user_id.id),
None => "".to_string(), None => String::new(),
}; };
let action = match state { let action = match state {
PackageState::Enabled => format!("pm list packages -s -e{user}"), PackageState::Enabled => format!("pm list packages -s -e{user}"),
PackageState::Disabled => format!("pm list package -s -d{user}"), PackageState::Disabled => format!("pm list package -s -d{user}"),
_ => "".to_string(), // You probably don't need to use this function for anything else _ => String::new(), // You probably don't need to use this function for anything else
}; };
adb_shell_command(true, &action) adb_shell_command(true, &action)
@ -178,7 +181,7 @@ pub struct CorePackage {
impl From<&mut PackageRow> for CorePackage { impl From<&mut PackageRow> for CorePackage {
fn from(pr: &mut PackageRow) -> Self { fn from(pr: &mut PackageRow) -> Self {
CorePackage { Self {
name: pr.name.clone(), name: pr.name.clone(),
state: pr.state, state: pr.state,
} }
@ -186,7 +189,7 @@ impl From<&mut PackageRow> for CorePackage {
} }
impl From<PackageRow> for CorePackage { impl From<PackageRow> for CorePackage {
fn from(pr: PackageRow) -> Self { fn from(pr: PackageRow) -> Self {
CorePackage { Self {
name: pr.name.clone(), name: pr.name.clone(),
state: pr.state, state: pr.state,
} }
@ -195,7 +198,7 @@ impl From<PackageRow> for CorePackage {
impl From<&PackageRow> for CorePackage { impl From<&PackageRow> for CorePackage {
fn from(pr: &PackageRow) -> Self { fn from(pr: &PackageRow) -> Self {
CorePackage { Self {
name: pr.name.clone(), name: pr.name.clone(),
state: pr.state, state: pr.state,
} }
@ -203,8 +206,8 @@ impl From<&PackageRow> for CorePackage {
} }
pub fn apply_pkg_state_commands( pub fn apply_pkg_state_commands(
package: CorePackage, package: &CorePackage,
wanted_state: &PackageState, wanted_state: PackageState,
selected_user: &User, selected_user: &User,
phone: &Phone, phone: &Phone,
) -> Vec<String> { ) -> Vec<String> {
@ -242,24 +245,25 @@ pub fn apply_pkg_state_commands(
}, },
_ => vec![], _ => vec![],
}, },
_ => vec![], PackageState::All => vec![],
}; };
if phone.android_sdk < 21 { if phone.android_sdk < 21 {
request_builder(commands, &package.name, None) request_builder(&commands, &package.name, None)
} else { } else {
request_builder(commands, &package.name, Some(selected_user)) request_builder(&commands, &package.name, Some(selected_user))
} }
} }
pub fn request_builder(commands: Vec<&str>, package: &str, user: Option<&User>) -> Vec<String> { pub fn request_builder(commands: &[&str], package: &str, user: Option<&User>) -> Vec<String> {
if let Some(u) = user { user.map_or_else(
commands || commands.iter().map(|c| format!("{c} {package}")).collect(),
.iter() |u| {
.map(|c| format!("{} --user {} {}", c, u.id, package)) commands
.collect() .iter()
} else { .map(|c| format!("{} --user {} {}", c, u.id, package))
commands.iter().map(|c| format!("{c} {package}")).collect() .collect()
} },
)
} }
pub fn get_phone_model() -> String { pub fn get_phone_model() -> String {
@ -277,17 +281,14 @@ pub fn get_phone_model() -> String {
} }
pub fn get_android_sdk() -> u8 { pub fn get_android_sdk() -> u8 {
match adb_shell_command(true, "getprop ro.build.version.sdk") { adb_shell_command(true, "getprop ro.build.version.sdk").map_or(0, |sdk| sdk.parse().unwrap())
Ok(sdk) => sdk.parse().unwrap(),
Err(_) => 0,
}
} }
pub fn get_phone_brand() -> String { pub fn get_phone_brand() -> String {
format!( format!(
"{} {}", "{} {}",
adb_shell_command(true, "getprop ro.product.brand") adb_shell_command(true, "getprop ro.product.brand")
.unwrap_or_else(|_| "".to_string()) .unwrap_or_else(|_| String::new())
.trim(), .trim(),
get_phone_model() get_phone_model()
) )
@ -300,6 +301,8 @@ pub fn is_protected_user(user_id: &str) -> bool {
pub fn get_user_list() -> Vec<User> { pub fn get_user_list() -> Vec<User> {
#[dynamic] #[dynamic]
static RE: Regex = Regex::new(r"\{([0-9]+)").unwrap(); static RE: Regex = Regex::new(r"\{([0-9]+)").unwrap();
#[allow(clippy::option_if_let_else)]
match adb_shell_command(true, "pm list users") { match adb_shell_command(true, "pm list users") {
Ok(users) => RE Ok(users) => RE
.find_iter(&users) .find_iter(&users)
@ -316,7 +319,7 @@ pub fn get_user_list() -> Vec<User> {
// getprop ro.serialno // getprop ro.serialno
pub async fn get_devices_list() -> Vec<Phone> { pub async fn get_devices_list() -> Vec<Phone> {
match retry( retry(
Fixed::from_millis(500).take(120), Fixed::from_millis(500).take(120),
|| match adb_shell_command(false, "devices") { || match adb_shell_command(false, "devices") {
Ok(devices) => { Ok(devices) => {
@ -341,8 +344,6 @@ pub async fn get_devices_list() -> Vec<Phone> {
OperationResult::Retry(test) OperationResult::Retry(test)
} }
}, },
) { )
Ok(devices) => devices, .map_or_else(|_| vec![], |devices| devices)
Err(_) => vec![],
}
} }

View file

@ -39,7 +39,7 @@ pub struct ColorPalette {
impl Theme { impl Theme {
pub const ALL: [Self; 3] = [Self::Lupin, Self::Dark, Self::Light]; pub const ALL: [Self; 3] = [Self::Lupin, Self::Dark, Self::Light];
pub fn palette(&self) -> ColorPalette { pub fn palette(self) -> ColorPalette {
match self { match self {
Self::Dark => ColorPalette { Self::Dark => ColorPalette {
base: BaseColors { base: BaseColors {
@ -105,9 +105,9 @@ impl std::fmt::Display for Theme {
f, f,
"{}", "{}",
match self { match self {
Theme::Dark => "Dark", Self::Dark => "Dark",
Theme::Light => "Light", Self::Light => "Light",
Theme::Lupin => "Lupin", Self::Lupin => "Lupin",
} }
) )
} }

View file

@ -44,24 +44,24 @@ impl std::fmt::Display for UadListState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let date = last_modified_date(CACHE_DIR.join("uad_lists.json")); let date = last_modified_date(CACHE_DIR.join("uad_lists.json"));
let s = match self { let s = match self {
UadListState::Downloading => "Checking updates...".to_string(), Self::Downloading => "Checking updates...".to_string(),
UadListState::Done => format!("Done (last was {})", format_diff_time_from_now(date)), Self::Done => format!("Done (last was {})", format_diff_time_from_now(date)),
UadListState::Failed => "Failed to check update!".to_string(), Self::Failed => "Failed to check update!".to_string(),
}; };
write!(f, "{s}") write!(f, "{s}")
} }
} }
impl UadList { impl UadList {
pub const ALL: [UadList; 8] = [ pub const ALL: [Self; 8] = [
UadList::All, Self::All,
UadList::Aosp, Self::Aosp,
UadList::Carrier, Self::Carrier,
UadList::Google, Self::Google,
UadList::Misc, Self::Misc,
UadList::Oem, Self::Oem,
UadList::Pending, Self::Pending,
UadList::Unlisted, Self::Unlisted,
]; ];
} }
@ -71,14 +71,14 @@ impl std::fmt::Display for UadList {
f, f,
"{}", "{}",
match self { match self {
UadList::All => "All lists", Self::All => "All lists",
UadList::Aosp => "aosp", Self::Aosp => "aosp",
UadList::Carrier => "carrier", Self::Carrier => "carrier",
UadList::Google => "google", Self::Google => "google",
UadList::Misc => "misc", Self::Misc => "misc",
UadList::Oem => "oem", Self::Oem => "oem",
UadList::Pending => "pending", Self::Pending => "pending",
UadList::Unlisted => "unlisted", Self::Unlisted => "unlisted",
} }
) )
} }
@ -94,12 +94,7 @@ pub enum PackageState {
} }
impl PackageState { impl PackageState {
pub const ALL: [PackageState; 4] = [ pub const ALL: [Self; 4] = [Self::All, Self::Enabled, Self::Uninstalled, Self::Disabled];
PackageState::All,
PackageState::Enabled,
PackageState::Uninstalled,
PackageState::Disabled,
];
} }
impl std::fmt::Display for PackageState { impl std::fmt::Display for PackageState {
@ -108,10 +103,10 @@ impl std::fmt::Display for PackageState {
f, f,
"{}", "{}",
match self { match self {
PackageState::All => "All packages", Self::All => "All packages",
PackageState::Enabled => "Enabled", Self::Enabled => "Enabled",
PackageState::Uninstalled => "Uninstalled", Self::Uninstalled => "Uninstalled",
PackageState::Disabled => "Disabled", Self::Disabled => "Disabled",
} }
) )
} }
@ -122,17 +117,17 @@ pub trait Opposite {
} }
impl Opposite for PackageState { impl Opposite for PackageState {
fn opposite(&self, disable: bool) -> PackageState { fn opposite(&self, disable: bool) -> Self {
match self { match self {
PackageState::Enabled => { Self::Enabled => {
if disable { if disable {
PackageState::Disabled Self::Disabled
} else { } else {
PackageState::Uninstalled Self::Uninstalled
} }
} }
PackageState::Uninstalled | PackageState::Disabled => PackageState::Enabled, Self::Uninstalled | Self::Disabled => Self::Enabled,
PackageState::All => PackageState::All, Self::All => Self::All,
} }
} }
} }
@ -150,13 +145,13 @@ pub enum Removal {
} }
impl Removal { impl Removal {
pub const ALL: [Removal; 6] = [ pub const ALL: [Self; 6] = [
Removal::All, Self::All,
Removal::Recommended, Self::Recommended,
Removal::Advanced, Self::Advanced,
Removal::Expert, Self::Expert,
Removal::Unsafe, Self::Unsafe,
Removal::Unlisted, Self::Unlisted,
]; ];
} }
@ -166,12 +161,12 @@ impl std::fmt::Display for Removal {
f, f,
"{}", "{}",
match self { match self {
Removal::All => "All", Self::All => "All",
Removal::Recommended => "Recommended", Self::Recommended => "Recommended",
Removal::Advanced => "Advanced", Self::Advanced => "Advanced",
Removal::Expert => "Expert", Self::Expert => "Expert",
Removal::Unsafe => "Unsafe", Self::Unsafe => "Unsafe",
Removal::Unlisted => "Unlisted", Self::Unlisted => "Unlisted",
} }
) )
} }
@ -182,10 +177,10 @@ pub fn load_debloat_lists(remote: bool) -> (Result<PackageHashMap, PackageHashMa
let cached_uad_lists: PathBuf = CACHE_DIR.join("uad_lists.json"); let cached_uad_lists: PathBuf = CACHE_DIR.join("uad_lists.json");
let mut error = false; let mut error = false;
let list: Vec<Package> = if remote { let list: Vec<Package> = if remote {
match retry(Fixed::from_millis(1000).take(60), || { retry(Fixed::from_millis(1000).take(60), || {
match ureq::get( match ureq::get(
"https://raw.githubusercontent.com/0x192/universal-android-debloater/\ "https://raw.githubusercontent.com/0x192/universal-android-debloater/\
main/resources/assets/uad_lists.json", main/resources/assets/uad_lists.json",
) )
.call() .call()
{ {
@ -201,10 +196,8 @@ pub fn load_debloat_lists(remote: bool) -> (Result<PackageHashMap, PackageHashMa
OperationResult::Retry(Vec::<Package>::new()) OperationResult::Retry(Vec::<Package>::new())
} }
} }
}) { })
Ok(list) => list, .map_or_else(|_| get_local_lists(), |list| list)
Err(_) => get_local_lists(),
}
} else { } else {
warn!("Could not load remote debloat list"); warn!("Could not load remote debloat list");
get_local_lists() get_local_lists()

View file

@ -41,10 +41,10 @@ pub enum SelfUpdateStatus {
impl std::fmt::Display for SelfUpdateStatus { impl std::fmt::Display for SelfUpdateStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self { let s = match self {
SelfUpdateStatus::Checking => "Checking updates...", Self::Checking => "Checking updates...",
SelfUpdateStatus::Updating => "Updating...", Self::Updating => "Updating...",
SelfUpdateStatus::Failed => "Failed to check update!", Self::Failed => "Failed to check update!",
SelfUpdateStatus::Done => "Done", Self::Done => "Done",
}; };
write!(f, "{s}") write!(f, "{s}")
} }
@ -52,7 +52,7 @@ impl std::fmt::Display for SelfUpdateStatus {
/// Download a file from the internet /// Download a file from the internet
#[cfg(feature = "self-update")] #[cfg(feature = "self-update")]
pub async fn download_file<T: ToString>(url: T, dest_file: PathBuf) -> Result<(), String> { pub async fn download_file<T: ToString + Send>(url: T, dest_file: PathBuf) -> Result<(), String> {
let url = url.to_string(); let url = url.to_string();
debug!("downloading file from {}", &url); debug!("downloading file from {}", &url);
@ -200,7 +200,7 @@ pub fn get_latest_release() -> Result<Option<Release>, ()> {
} }
} }
/// Extracts the binary from a `tar.gz` archive to temp_file path /// Extracts the binary from a `tar.gz` archive to `temp_file` path
#[cfg(feature = "self-update")] #[cfg(feature = "self-update")]
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
pub fn extract_binary_from_tar(archive_path: &Path, temp_file: &Path) -> io::Result<()> { pub fn extract_binary_from_tar(archive_path: &Path, temp_file: &Path) -> io::Result<()> {

View file

@ -31,7 +31,7 @@ pub fn fetch_packages(
if uad_lists.contains_key(p_name) { if uad_lists.contains_key(p_name) {
description = &uad_lists.get(p_name).unwrap().description; description = &uad_lists.get(p_name).unwrap().description;
if description.is_empty() { if description.is_empty() {
description = "[No description] : CONTRIBUTION WELCOMED" description = "[No description] : CONTRIBUTION WELCOMED";
}; };
uad_list = uad_lists.get(p_name).unwrap().list; uad_list = uad_lists.get(p_name).unwrap().list;
removal = uad_lists.get(p_name).unwrap().removal; removal = uad_lists.get(p_name).unwrap().removal;
@ -51,8 +51,8 @@ pub fn fetch_packages(
user_package user_package
} }
pub fn string_to_theme(theme: String) -> Theme { pub fn string_to_theme(theme: &str) -> Theme {
match theme.as_str() { match theme {
"Dark" => Theme::Dark, "Dark" => Theme::Dark,
"Light" => Theme::Light, "Light" => Theme::Light,
"Lupin" => Theme::Lupin, "Lupin" => Theme::Lupin,
@ -80,21 +80,20 @@ pub fn open_url(dir: PathBuf) {
Ok(o) => { Ok(o) => {
if !o.status.success() { if !o.status.success() {
let stderr = String::from_utf8(o.stderr).unwrap().trim_end().to_string(); let stderr = String::from_utf8(o.stderr).unwrap().trim_end().to_string();
error!("Can't open the following URL: {}", stderr) error!("Can't open the following URL: {}", stderr);
} }
} }
Err(e) => error!("Failed to run command to open the file explorer: {}", e), Err(e) => error!("Failed to run command to open the file explorer: {}", e),
} }
} }
#[rustfmt::skip]
#[allow(clippy::option_if_let_else)]
pub fn last_modified_date(file: PathBuf) -> DateTime<Utc> { pub fn last_modified_date(file: PathBuf) -> DateTime<Utc> {
match fs::metadata(file) { fs::metadata(file).map_or_else(|_| Utc::now(), |metadata| match metadata.modified() {
Ok(metadata) => match metadata.modified() { Ok(time) => time.into(),
Ok(time) => time.into(),
Err(_) => Utc::now(),
},
Err(_) => Utc::now(), Err(_) => Utc::now(),
} })
} }
pub fn format_diff_time_from_now(date: DateTime<Utc>) -> String { pub fn format_diff_time_from_now(date: DateTime<Utc>) -> String {
@ -118,19 +117,20 @@ pub struct DisplayablePath {
impl fmt::Display for DisplayablePath { impl fmt::Display for DisplayablePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let stem = match self.path.file_stem() { let stem = self.path.file_stem().map_or_else(
Some(p) => match p.to_os_string().into_string() { || {
error!("[PATH STEM]: No file stem found");
"[File steam not found]".to_string()
},
|p| match p.to_os_string().into_string() {
Ok(stem) => stem, Ok(stem) => stem,
Err(e) => { Err(e) => {
error!("[PATH ENCODING]: {:?}", e); error!("[PATH ENCODING]: {:?}", e);
"[PATH ENCODING ERROR]".to_string() "[PATH ENCODING ERROR]".to_string()
} }
}, },
None => { );
error!("[PATH STEM]: No file stem found");
"[File steam not found]".to_string()
}
};
write!(f, "{stem}") write!(f, "{stem}")
} }
} }

View file

@ -87,7 +87,7 @@ impl Application for UadGui {
} }
fn theme(&self) -> Theme { fn theme(&self) -> Theme {
string_to_theme(self.settings_view.general.theme.clone()) string_to_theme(&self.settings_view.general.theme)
} }
fn title(&self) -> String { fn title(&self) -> String {
@ -95,6 +95,7 @@ impl Application for UadGui {
} }
fn update(&mut self, msg: Message) -> Command<Message> { fn update(&mut self, msg: Message) -> Command<Message> {
match msg { match msg {
#[allow(clippy::option_if_let_else)]
Message::LoadDevices(devices_list) => { Message::LoadDevices(devices_list) => {
self.selected_device = match &self.selected_device { self.selected_device = match &self.selected_device {
Some(s_device) => { Some(s_device) => {
@ -102,9 +103,9 @@ impl Application for UadGui {
devices_list devices_list
.iter() .iter()
.find(|phone| phone.adb_id == s_device.adb_id) .find(|phone| phone.adb_id == s_device.adb_id)
.map(|x| x.to_owned()) .cloned()
} }
None => devices_list.first().map(|x| x.to_owned()), None => devices_list.first().cloned(),
}; };
self.devices_list = devices_list; self.devices_list = devices_list;
@ -210,7 +211,7 @@ impl Application for UadGui {
AboutMessage::UpdateUadLists => { AboutMessage::UpdateUadLists => {
self.update_state.uad_list = UadListState::Downloading; self.update_state.uad_list = UadListState::Downloading;
self.apps_view.loading_state = self.apps_view.loading_state =
ListLoadingState::DownloadingList("".to_string()); ListLoadingState::DownloadingList(String::new());
self.update(Message::AppsAction(AppsMessage::LoadUadList(true))) self.update(Message::AppsAction(AppsMessage::LoadUadList(true)))
} }
AboutMessage::DoSelfUpdate => { AboutMessage::DoSelfUpdate => {
@ -218,7 +219,7 @@ impl Application for UadGui {
if self.update_state.self_update.latest_release.is_some() { if self.update_state.self_update.latest_release.is_some() {
self.update_state.self_update.status = SelfUpdateStatus::Updating; self.update_state.self_update.status = SelfUpdateStatus::Updating;
self.apps_view.loading_state = self.apps_view.loading_state =
ListLoadingState::_UpdatingUad("".to_string()); ListLoadingState::_UpdatingUad(String::new());
let bin_name = bin_name().to_owned(); let bin_name = bin_name().to_owned();
let release = self let release = self
.update_state .update_state
@ -237,7 +238,7 @@ impl Application for UadGui {
#[cfg(not(feature = "self-update"))] #[cfg(not(feature = "self-update"))]
Command::none() Command::none()
} }
_ => Command::none(), AboutMessage::UrlPressed(_) => Command::none(),
} }
} }
Message::DeviceSelected(s_device) => { Message::DeviceSelected(s_device) => {
@ -250,7 +251,7 @@ impl Application for UadGui {
s_device.android_sdk, s_device.model s_device.android_sdk, s_device.model
); );
info!("{:-^65}", "-"); info!("{:-^65}", "-");
self.apps_view.loading_state = ListLoadingState::FindingPhones("".to_string()); self.apps_view.loading_state = ListLoadingState::FindingPhones(String::new());
#[allow(unused_must_use)] #[allow(unused_must_use)]
{ {
@ -261,48 +262,46 @@ impl Application for UadGui {
UadListState::Done, UadListState::Done,
)))) ))))
} }
Message::_NewReleaseDownloaded(_res) => { Message::_NewReleaseDownloaded(res) => {
debug!("UAD update has been download!"); debug!("UAD update has been download!");
#[cfg(feature = "self-update")] if let Ok((relaunch_path, cleanup_path)) = res {
match _res { // Remove first arg, which is path to binary. We don't use this first
Ok((relaunch_path, cleanup_path)) => { // arg as binary path because it's not reliable, per the docs.
// Remove first arg, which is path to binary. We don't use this first let mut args = std::env::args();
// arg as binary path because it's not reliable, per the docs. args.next();
let mut args = std::env::args(); let mut args: Vec<_> = args.collect();
args.next();
let mut args: Vec<_> = args.collect();
// Remove the `--self-update-temp` arg from args if it exists, // Remove the `--self-update-temp` arg from args if it exists,
// since we need to pass it cleanly. Otherwise new process will // since we need to pass it cleanly. Otherwise new process will
// fail during arg parsing. // fail during arg parsing.
if let Some(idx) = args.iter().position(|a| a == "--self-update-temp") { if let Some(idx) = args.iter().position(|a| a == "--self-update-temp") {
args.remove(idx); args.remove(idx);
// Remove path passed after this arg // Remove path passed after this arg
args.remove(idx); args.remove(idx);
}
match std::process::Command::new(relaunch_path)
.args(args)
.arg("--self-update-temp")
.arg(&cleanup_path)
.spawn()
{
Ok(_) => {
if let Err(e) = remove_file(cleanup_path) {
error!("Could not remove temp update file: {}", e);
}
std::process::exit(0)
} }
Err(error) => {
match std::process::Command::new(relaunch_path) if let Err(e) = remove_file(cleanup_path) {
.args(args) error!("Could not remove temp update file: {}", e);
.arg("--self-update-temp")
.arg(&cleanup_path)
.spawn()
{
Ok(_) => {
if let Err(e) = remove_file(cleanup_path) {
error!("Could not remove temp update file: {}", e);
}
std::process::exit(0)
}
Err(error) => {
if let Err(e) = remove_file(cleanup_path) {
error!("Could not remove temp update file: {}", e);
}
error!("Failed to update UAD: {}", error)
} }
error!("Failed to update UAD: {}", error);
} }
} }
Err(()) => error!("Failed to update UAD!"), } else {
error!("Failed to update UAD!");
} }
Command::none() Command::none()
} }
@ -310,13 +309,13 @@ impl Application for UadGui {
match release { match release {
Ok(r) => { Ok(r) => {
self.update_state.self_update.status = SelfUpdateStatus::Done; self.update_state.self_update.status = SelfUpdateStatus::Done;
self.update_state.self_update.latest_release = r self.update_state.self_update.latest_release = r;
} }
Err(_) => self.update_state.self_update.status = SelfUpdateStatus::Failed, Err(_) => self.update_state.self_update.status = SelfUpdateStatus::Failed,
}; };
Command::none() Command::none()
} }
_ => Command::none(), Message::Nothing => Command::none(),
} }
} }

View file

@ -102,11 +102,9 @@ impl button::StyleSheet for Theme {
}; };
match style { match style {
Button::Primary => active_appearance(None, p.bright.primary), Button::Primary | Button::SelfUpdate | Button::Refresh => {
Button::Unavailable => active_appearance(None, p.bright.error), active_appearance(None, p.bright.primary)
Button::Refresh => active_appearance(None, p.bright.primary), }
Button::SelfUpdate => active_appearance(None, p.bright.primary),
Button::UninstallPackage => active_appearance(None, p.bright.error),
Button::RestorePackage => active_appearance(None, p.bright.secondary), Button::RestorePackage => active_appearance(None, p.bright.secondary),
Button::NormalPackage => button::Appearance { Button::NormalPackage => button::Appearance {
background: Some(Background::Color(p.base.foreground)), background: Some(Background::Color(p.base.foreground)),
@ -127,6 +125,9 @@ impl button::StyleSheet for Theme {
border_color: p.normal.primary, border_color: p.normal.primary,
..appearance ..appearance
}, },
Button::Unavailable | Button::UninstallPackage => {
active_appearance(None, p.bright.error)
}
} }
} }
@ -141,17 +142,22 @@ impl button::StyleSheet for Theme {
}; };
match style { match style {
Button::Primary => hover_appearance(p.bright.primary, None), Button::Primary | Button::SelfUpdate | Button::Refresh => {
Button::Unavailable => hover_appearance(p.bright.error, None), hover_appearance(p.bright.primary, None)
Button::Refresh => hover_appearance(p.bright.primary, None), }
Button::SelfUpdate => hover_appearance(p.bright.primary, None),
Button::UninstallPackage => hover_appearance(p.bright.error, None),
Button::RestorePackage => hover_appearance(p.bright.secondary, None),
Button::NormalPackage => hover_appearance(p.normal.primary, Some(p.bright.surface)), Button::NormalPackage => hover_appearance(p.normal.primary, Some(p.bright.surface)),
Button::SelectedPackage => hover_appearance(p.normal.primary, None), Button::SelectedPackage => hover_appearance(p.normal.primary, None),
Button::RestorePackage => hover_appearance(p.bright.secondary, None),
Button::Unavailable | Button::UninstallPackage => {
hover_appearance(p.bright.error, None)
}
} }
} }
fn pressed(&self, style: &Self::Style) -> button::Appearance {
self.active(style)
}
fn disabled(&self, style: &Self::Style) -> button::Appearance { fn disabled(&self, style: &Self::Style) -> button::Appearance {
let active = self.active(style); let active = self.active(style);
let p = self.palette(); let p = self.palette();
@ -169,13 +175,7 @@ impl button::StyleSheet for Theme {
Button::RestorePackage => disabled_appearance(p.normal.primary, Some(p.bright.primary)), Button::RestorePackage => disabled_appearance(p.normal.primary, Some(p.bright.primary)),
Button::UninstallPackage => disabled_appearance(p.bright.error, None), Button::UninstallPackage => disabled_appearance(p.bright.error, None),
Button::Primary => disabled_appearance(p.bright.primary, Some(p.bright.primary)), Button::Primary => disabled_appearance(p.bright.primary, Some(p.bright.primary)),
_ => button::Appearance { ..active }, _ => active,
}
}
fn pressed(&self, style: &Self::Style) -> button::Appearance {
button::Appearance {
..self.active(style)
} }
} }
} }
@ -212,9 +212,7 @@ impl scrollable::StyleSheet for Theme {
fn hovered(&self, style: &Self::Style, _mouse_over_scrollbar: bool) -> scrollable::Scrollbar { fn hovered(&self, style: &Self::Style, _mouse_over_scrollbar: bool) -> scrollable::Scrollbar {
scrollable::Scrollbar { scrollable::Scrollbar {
scroller: scrollable::Scroller { scroller: self.active(style).scroller,
..self.active(style).scroller
},
..self.active(style) ..self.active(style)
} }
} }
@ -222,7 +220,7 @@ impl scrollable::StyleSheet for Theme {
fn dragging(&self, style: &Self::Style) -> scrollable::Scrollbar { fn dragging(&self, style: &Self::Style) -> scrollable::Scrollbar {
let hovered = self.hovered(style, true); let hovered = self.hovered(style, true);
scrollable::Scrollbar { scrollable::Scrollbar {
scroller: scrollable::Scroller { ..hovered.scroller }, scroller: hovered.scroller,
..hovered ..hovered
} }
} }
@ -291,10 +289,10 @@ impl checkbox::StyleSheet for Theme {
}; };
match style { match style {
CheckBox::PackageEnabled => from_appearance(), CheckBox::PackageEnabled | CheckBox::SettingsEnabled => from_appearance(),
CheckBox::SettingsEnabled => from_appearance(), CheckBox::PackageDisabled | CheckBox::SettingsDisabled => {
CheckBox::PackageDisabled => self.active(style, is_checked), self.active(style, is_checked)
CheckBox::SettingsDisabled => self.active(style, is_checked), }
} }
} }
} }
@ -410,7 +408,7 @@ pub enum Text {
impl From<Color> for Text { impl From<Color> for Text {
fn from(color: Color) -> Self { fn from(color: Color) -> Self {
Text::Color(color) Self::Color(color)
} }
} }
@ -419,7 +417,7 @@ impl text::StyleSheet for Theme {
fn appearance(&self, style: Self::Style) -> text::Appearance { fn appearance(&self, style: Self::Style) -> text::Appearance {
match style { match style {
Text::Default => Default::default(), Text::Default => text::Appearance::default(),
Text::Ok => text::Appearance { Text::Ok => text::Appearance {
color: Some(self.palette().bright.secondary), color: Some(self.palette().bright.secondary),
}, },

View file

@ -56,22 +56,17 @@ impl About {
text(format!("UAD version: v{}", env!("CARGO_PKG_VERSION"))).width(250); text(format!("UAD version: v{}", env!("CARGO_PKG_VERSION"))).width(250);
#[cfg(feature = "self-update")] #[cfg(feature = "self-update")]
let self_update_text = match &update_state.self_update.latest_release { #[rustfmt::skip]
Some(r) => { let self_update_text = update_state.self_update.latest_release.as_ref().map_or_else(||
if update_state.self_update.status == SelfUpdateStatus::Updating { if update_state.self_update.status == SelfUpdateStatus::Done {
update_state.self_update.status.to_string() "(No update available)".to_string()
} else { } else {
format!("(v{} available)", r.tag_name) update_state.self_update.status.to_string()
} }, |r| if update_state.self_update.status == SelfUpdateStatus::Updating {
} update_state.self_update.status.to_string()
None => { } else {
if update_state.self_update.status == SelfUpdateStatus::Done { format!("(v{} available)", r.tag_name)
"(No update available)".to_string() });
} else {
update_state.self_update.status.to_string()
}
}
};
#[cfg(feature = "self-update")] #[cfg(feature = "self-update")]
let last_self_update_text = text(self_update_text).style(style::Text::Default); let last_self_update_text = text(self_update_text).style(style::Text::Default);

View file

@ -38,7 +38,7 @@ pub enum LoadingState {
impl Default for LoadingState { impl Default for LoadingState {
fn default() -> Self { fn default() -> Self {
Self::FindingPhones("".to_string()) Self::FindingPhones(String::new())
} }
} }
@ -96,7 +96,7 @@ impl List {
} }
Message::ModalValidate => { Message::ModalValidate => {
let mut commands = vec![]; let mut commands = vec![];
self.selected_packages.sort(); self.selected_packages.sort_unstable();
self.selected_packages.dedup(); self.selected_packages.dedup();
for selection in &self.selected_packages { for selection in &self.selected_packages {
commands.append(&mut build_action_pkg_commands( commands.append(&mut build_action_pkg_commands(
@ -114,7 +114,7 @@ impl List {
if let CommandType::PackageManager(p) = res { if let CommandType::PackageManager(p) = res {
self.loading_state = LoadingState::RestoringDevice( self.loading_state = LoadingState::RestoringDevice(
self.phone_packages[i_user][p.index].name.clone(), self.phone_packages[i_user][p.index].name.clone(),
) );
} }
} else { } else {
self.loading_state = LoadingState::RestoringDevice("Error [TODO]".to_string()); self.loading_state = LoadingState::RestoringDevice("Error [TODO]".to_string());
@ -128,7 +128,7 @@ impl List {
selected_device.android_sdk, selected_device.model selected_device.android_sdk, selected_device.model
); );
info!("{:-^65}", "-"); info!("{:-^65}", "-");
self.loading_state = LoadingState::DownloadingList("".to_string()); self.loading_state = LoadingState::DownloadingList(String::new());
Command::perform( Command::perform(
Self::init_apps_view(remote, selected_device.clone()), Self::init_apps_view(remote, selected_device.clone()),
Message::LoadPhonePackages, Message::LoadPhonePackages,
@ -136,7 +136,7 @@ impl List {
} }
Message::LoadPhonePackages(list_box) => { Message::LoadPhonePackages(list_box) => {
let (uad_list, list_state) = list_box; let (uad_list, list_state) = list_box;
self.loading_state = LoadingState::LoadingPackages("".to_string()); self.loading_state = LoadingState::LoadingPackages(String::new());
self.uad_lists = uad_list.clone(); self.uad_lists = uad_list.clone();
*list_update_state = list_state; *list_update_state = list_state;
Command::perform( Command::perform(
@ -152,7 +152,7 @@ impl List {
self.selected_list = Some(UadList::All); self.selected_list = Some(UadList::All);
self.selected_user = Some(User::default()); self.selected_user = Some(User::default());
Self::filter_package_lists(self); Self::filter_package_lists(self);
self.loading_state = LoadingState::Ready("".to_string()); self.loading_state = LoadingState::Ready(String::new());
Command::none() Command::none()
} }
Message::ToggleAllSelected(selected) => { Message::ToggleAllSelected(selected) => {
@ -193,7 +193,7 @@ impl List {
#[allow(unused_must_use)] #[allow(unused_must_use)]
{ {
self.phone_packages[i_user][i_package] self.phone_packages[i_user][i_package]
.update(row_message.clone()) .update(&row_message)
.map(move |row_message| Message::List(i_package, row_message)); .map(move |row_message| Message::List(i_package, row_message));
} }
@ -600,7 +600,7 @@ impl List {
text("Enable").style(style::Text::Ok), text("Enable").style(style::Text::Ok),
PackageState::Uninstalled => PackageState::Uninstalled =>
text("Restore").style(style::Text::Ok), text("Restore").style(style::Text::Ok),
_ => text("Impossible") PackageState::All => text("Impossible")
.style(style::Text::Danger), .style(style::Text::Danger),
},] },]
.width(60), .width(60),
@ -625,13 +625,7 @@ impl List {
.padding([0, 10, 0, 10]); .padding([0, 10, 0, 10]);
container( container(
if device if device.user_list.iter().filter(|&u| !u.protected).count() > 1
.user_list
.iter()
.filter(|&u| !u.protected)
.collect::<Vec<&User>>()
.len()
> 1
&& settings.device.multi_user_mode && settings.device.multi_user_mode
{ {
column![ column![
@ -696,13 +690,13 @@ impl List {
let mut phone_packages = vec![]; let mut phone_packages = vec![];
if user_list.len() <= 1 { if user_list.len() <= 1 {
phone_packages.push(fetch_packages(&uad_list, None)) phone_packages.push(fetch_packages(&uad_list, None));
} else { } else {
phone_packages.extend( phone_packages.extend(
user_list user_list
.iter() .iter()
.map(|user| fetch_packages(&uad_list, Some(user))), .map(|user| fetch_packages(&uad_list, Some(user))),
) );
}; };
phone_packages phone_packages
} }
@ -775,10 +769,10 @@ fn build_action_pkg_commands(
}) { }) {
let u_pkg = packages[u.index][selection.1].clone(); let u_pkg = packages[u.index][selection.1].clone();
let actions = if settings.multi_user_mode { let actions = if settings.multi_user_mode {
apply_pkg_state_commands(u_pkg.into(), &wanted_state, u, device) apply_pkg_state_commands(&u_pkg.into(), wanted_state, u, device)
} else { } else {
let wanted_state = &u_pkg.state.opposite(settings.disable_mode); let wanted_state = u_pkg.state.opposite(settings.disable_mode);
apply_pkg_state_commands(u_pkg.into(), wanted_state, u, device) apply_pkg_state_commands(&u_pkg.into(), wanted_state, u, device)
}; };
for (j, action) in actions.into_iter().enumerate() { for (j, action) in actions.into_iter().enumerate() {
let p_info = PackageInfo { let p_info = PackageInfo {

View file

@ -96,7 +96,7 @@ impl Settings {
selected: backups.first().cloned(), selected: backups.first().cloned(),
users: phone.user_list.clone(), users: phone.user_list.clone(),
selected_user: phone.user_list.first().copied(), selected_user: phone.user_list.first().copied(),
backup_state: "".to_string(), backup_state: String::new(),
}; };
} }
None => { None => {
@ -109,7 +109,7 @@ impl Settings {
selected: backups.first().cloned(), selected: backups.first().cloned(),
users: phone.user_list.clone(), users: phone.user_list.clone(),
selected_user: phone.user_list.first().copied(), selected_user: phone.user_list.first().copied(),
backup_state: "".to_string(), backup_state: String::new(),
}, },
} }
} }
@ -190,7 +190,7 @@ impl Settings {
radio( radio(
format!("{}", option.clone()), format!("{}", option.clone()),
*option, *option,
Some(string_to_theme(self.general.theme.clone())), Some(string_to_theme(&self.general.theme)),
Message::ApplyTheme, Message::ApplyTheme,
) )
.size(23), .size(23),
@ -224,7 +224,7 @@ impl Settings {
row![ row![
text("The following settings only affect the currently selected device :") text("The following settings only affect the currently selected device :")
.style(style::Text::Danger), .style(style::Text::Danger),
text(phone.model.to_owned()), text(phone.model.clone()),
Space::new(Length::Fill, Length::Shrink), Space::new(Length::Fill, Length::Shrink),
text(phone.adb_id.clone()).style(style::Text::Commentary) text(phone.adb_id.clone()).style(style::Text::Commentary)
] ]
@ -390,20 +390,7 @@ impl Settings {
.style(style::Container::BorderedFrame) .style(style::Container::BorderedFrame)
}; };
let content = if !phone.adb_id.clone().is_empty() { let content = if phone.adb_id.clone().is_empty() {
column![
text("Theme").size(25),
theme_ctn,
text("General").size(25),
general_ctn,
text("Current device").size(25),
warning_ctn,
device_specific_ctn,
backup_restore_ctn,
]
.width(Length::Fill)
.spacing(20)
} else {
column![ column![
text("Theme").size(25), text("Theme").size(25),
theme_ctn, theme_ctn,
@ -416,6 +403,19 @@ impl Settings {
] ]
.width(Length::Fill) .width(Length::Fill)
.spacing(20) .spacing(20)
} else {
column![
text("Theme").size(25),
theme_ctn,
text("General").size(25),
general_ctn,
text("Current device").size(25),
warning_ctn,
device_specific_ctn,
backup_restore_ctn,
]
.width(Length::Fill)
.spacing(20)
}; };
container(content) container(content)

View file

@ -25,6 +25,7 @@ impl<'a, Message, Renderer> Modal<'a, Message, Renderer> {
} }
} }
#[allow(clippy::missing_const_for_fn)]
/// Sets the message that will be produces when the background /// Sets the message that will be produces when the background
/// of the [`Modal`] is pressed /// of the [`Modal`] is pressed
pub fn on_blur(self, on_blur: Message) -> Self { pub fn on_blur(self, on_blur: Message) -> Self {
@ -187,6 +188,7 @@ where
) -> event::Status { ) -> event::Status {
let content_bounds = layout.children().next().unwrap().bounds(); let content_bounds = layout.children().next().unwrap().bounds();
#[allow(clippy::equatable_if_let)]
if let Some(message) = self.on_blur.as_ref() { if let Some(message) = self.on_blur.as_ref() {
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) = &event { if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) = &event {
if !content_bounds.contains(cursor_position) { if !content_bounds.contains(cursor_position) {

View file

@ -34,6 +34,7 @@ pub fn nav_menu<'a>(
.padding(5) .padding(5)
.style(style::Button::Refresh); .style(style::Button::Refresh);
#[allow(clippy::option_if_let_else)]
let uad_version_text = if let Some(r) = &self_update_state.latest_release { let uad_version_text = if let Some(r) = &self_update_state.latest_release {
if self_update_state.status == SelfUpdateStatus::Updating { if self_update_state.status == SelfUpdateStatus::Updating {
Text::new("Updating please wait...") Text::new("Updating please wait...")
@ -48,17 +49,17 @@ pub fn nav_menu<'a>(
Text::new(env!("CARGO_PKG_VERSION")) Text::new(env!("CARGO_PKG_VERSION"))
}; };
let mut apps_btn = button("Apps") let apps_btn = if self_update_state.latest_release.is_some() {
.on_press(Message::AppsPress) button("Update")
.padding(5)
.style(style::Button::Primary);
if self_update_state.latest_release.is_some() {
apps_btn = button("Update")
.on_press(Message::AboutAction(AboutMessage::DoSelfUpdate)) .on_press(Message::AboutAction(AboutMessage::DoSelfUpdate))
.padding(5) .padding(5)
.style(style::Button::SelfUpdate); .style(style::Button::SelfUpdate)
} } else {
button("Apps")
.on_press(Message::AppsPress)
.padding(5)
.style(style::Button::Primary)
};
let about_btn = button("About") let about_btn = button("About")
.on_press(Message::AboutPressed) .on_press(Message::AboutPressed)

View file

@ -46,7 +46,7 @@ impl PackageRow {
} }
} }
pub fn update(&mut self, _message: Message) -> Command<Message> { pub fn update(&mut self, _message: &Message) -> Command<Message> {
Command::none() Command::none()
} }

View file

@ -42,7 +42,7 @@ pub fn setup_logger() -> Result<(), fern::InitError> {
record.file().unwrap_or("?"), record.file().unwrap_or("?"),
record.line().map(|l| l.to_string()).unwrap_or_default(), record.line().map(|l| l.to_string()).unwrap_or_default(),
message message
)) ));
} }
}; };