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 rows
  • crates/uad-gui/src/theme.rs – Defines colors, fonts, and styling
  • crates/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 options
  • uninstall – Remove specific packages
  • enable / disable – Toggle package states
  • update – 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:

  1. Action Initiation – The GUI sends a message through the Iced application state, or the CLI parses the sub-command in main.rs
  2. Core Delegation – Both call commands::change_package_state (CLI) or the analogous core function (GUI)
  3. ADB Execution – The core function interacts with adb.rs to execute adb shell pm uninstall ...
  4. 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:

  1. Launch the application (cargo run --bin uad-gui)
  2. Navigate to the Packages view, populated by uad_core::uad_lists
  3. Select a package and click Uninstall
  4. The widget handler in crates/uad-gui/src/widgets/package_row.rs calls uad_core::save::uninstall_package
  5. 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 in adb.rs, package lists in uad_lists.rs, state changes in save.rs, and configuration in config.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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →