UAD GUI vs CLI: Differences and Shared Core Logic Explained
The Universal Android Debloater Next Generation provides both a graphical interface built with Iced and a terminal interface powered by clap, with both front-ends delegating all device communication and package management to a shared uad-core crate.
The Universal Android Debloater Next Generation (UAD NG) is organized as a Rust workspace containing three distinct crates: uad-gui, uad-cli, and uad-core. While the GUI and CLI components present different user experiences, they rely on the exact same underlying logic for ADB communication, package filtering, and state management. This architecture ensures consistent behavior whether you prefer clicking through a modern interface or scripting terminal commands.
GUI Component Architecture
The graphical interface (uad-gui) delivers a desktop application experience using the Iced UI library for Rust.
Entry Point and Event Loop
The GUI initializes in crates/uad-gui/src/main.rs, which creates an Iced Application and enters the event loop. Unlike the CLI, the GUI runs on a single UI thread, delegating asynchronous work to tokio tasks inside Iced's Command system.
Visual Components
Rich visual elements are defined across several modules:
crates/uad-gui/src/widgets/– Contains reusable UI components like lists and package rowscrates/uad-gui/src/theme.rs– Defines colors, fonts, and stylingcrates/uad-gui/src/views/– Implements screens such as the package list, settings dialog, and about page
When a user clicks the Uninstall button in the interface, the handler in crates/uad-gui/src/widgets/package_row.rs triggers the core logic. The GUI renders tooltips, icons, and status indicators while background tasks execute device commands.
CLI Component Architecture
The command-line interface (uad-cli) provides a text-based experience built with clap for argument parsing and tokio for async execution.
Entry Point and Command Dispatch
crates/uad-cli/src/main.rs serves as the entry point, using clap::Parser to process arguments and dispatch to sub-commands via a match cli.command block. The CLI runs as a standard async binary with #[tokio::main], spawning tasks as needed for concurrent operations.
Command Structure
The CLI operates through explicit sub-commands:
list– Display packages with filtering optionsuninstall– Remove specific packagesenable/disable– Toggle package statesupdate– Refresh the debloat list from GitHub
Implementation details for each command reside in crates/uad-cli/src/commands.rs, which provides thin wrappers around uad-core functionality. Output formatting is handled separately in crates/uad-cli/src/output.rs, ensuring plain text suitable for piping and scripting.
Interactive Mode
Beyond single commands, crates/uad-cli/src/repl.rs implements an interactive REPL mode for users who prefer a shell-like experience within the terminal.
How GUI and CLI Share Core Logic
Both front-ends are thin clients that delegate all substantive work to the uad-core crate. This shared library contains pure Rust modules with no UI assumptions, making it environment-agnostic.
Core Functionality Map
| Feature | Implementation File | Used By |
|---|---|---|
| ADB Communication | crates/uad-core/src/adb.rs |
Both GUI and CLI |
| Package List Management | crates/uad-core/src/uad_lists.rs |
Both GUI and CLI |
| State Changes | crates/uad-core/src/save.rs & src/sync.rs |
Both GUI and CLI |
| Configuration | crates/uad-core/src/config.rs |
Both GUI and CLI |
| Update Checking | crates/uad-core/src/update.rs |
Both GUI and CLI |
Typical Execution Flow
When you initiate a package removal, the execution path follows the same core logic regardless of interface:
- Action Initiation – The GUI sends a message through the Iced application state, or the CLI parses the sub-command in
main.rs - Core Delegation – Both call
commands::change_package_state(CLI) or the analogous core function (GUI) - ADB Execution – The core function interacts with
adb.rsto executeadb shell pm uninstall ... - Result Handling – The GUI updates the widget state, while the CLI prints a status line via
output.rs
Configuration Sharing
Both front-ends read from the same configuration files (config.toml, uad_lists.json) through uad_core::config. This ensures that settings like device preferences and list sources remain synchronized across usage modes.
Practical Usage Examples
CLI Package Listing
List removable packages on the first connected device using the terminal:
uad list --removal removable
The list sub-command in crates/uad-cli/src/commands.rs parses these options and forwards them to commands::list_packages, which internally calls uad_core::uad_lists to retrieve and filter the package data.
GUI Package Removal
Remove a package through the graphical interface:
- Launch the application (
cargo run --bin uad-gui) - Navigate to the Packages view, populated by
uad_core::uad_lists - Select a package and click Uninstall
- The widget handler in
crates/uad-gui/src/widgets/package_row.rscallsuad_core::save::uninstall_package - The UI refreshes to show the package status as Uninstalled
Both workflows execute identical ADB commands through the shared core, ensuring consistent results.
Summary
- GUI (
uad-gui): Built with Iced, provides visual package browsing with widgets and themed windows, running on a single UI thread with async task delegation - CLI (
uad-cli): Built with clap and tokio, offers sub-command-based automation and REPL mode, outputting plain text for terminal use - Shared Core (
uad-core): Houses all ADB communication inadb.rs, package lists inuad_lists.rs, state changes insave.rs, and configuration inconfig.rs - Consistency: Both interfaces use identical logic for device operations, ensuring the same debloating behavior across graphical and terminal environments
Frequently Asked Questions
Can I use the GUI and CLI interchangeably on the same device?
Yes. Both front-ends interact with the same Android Debug Bridge (ADB) server and use identical logic from uad_core. You can configure packages using the GUI and later script operations using the CLI without conflicts, as both read from the same config.toml and uad_lists.json files.
Which component actually executes the ADB commands?
Neither the GUI nor CLI executes ADB commands directly. Both delegate to crates/uad-core/src/adb.rs, which wraps the adb binary and handles device discovery, command execution, and error handling. This centralized approach ensures consistent command formatting and device communication across all interfaces.
How do the GUI and CLI handle asynchronous operations differently?
The CLI uses #[tokio::main] with standard async task spawning throughout the command execution. The GUI runs on a single UI thread managed by Iced, using tokio tasks specifically within Iced's Command system to perform background work without blocking the interface. Both rely on the same tokio runtime but integrate it according to their respective framework requirements.
Is it possible to add a web interface using the same core logic?
Yes. Because uad-core contains pure Rust modules with no UI dependencies, it can be imported into any environment. A web front-end could import uad_core::adb, uad_core::uad_lists, and uad_core::save to leverage the existing device communication and package management logic without duplicating code.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →