Introduction

Android DEX Documentation

A comprehensive guide to understanding the Android DEX codebase. This documentation covers the architecture, file structure, core services, and data flow that power the wireless desktop experience.

Flutter Desktop

Cross-platform app built with Flutter for Windows, macOS, and Linux

TCP Server

Real-time communication with Android devices over local network

scrcpy Integration

Display mirroring and virtual display creation via scrcpy

System Architecture

The Android DEX system consists of three main layers that work together to provide the desktop experience.

Layer 1

Presentation Layer

HomeScreen
GlassCard
DeviceList

Flutter widgets handling user interface and interactions

Layer 2

Service Layer

AdbTcpServer
ScrcpyService
AudioManager

Business logic handling connections and device control

Layer 3

External Tools

ADB
scrcpy
mDNS

Command-line tools for Android communication

Connection Flow

1
User Input Enter device IP or select from discovered devices
2
ADB Connect Establish wireless ADB connection on port 5555
3
scrcpy Launch Create virtual 1920x1080 display with scrcpy
4
TCP Handshake Android app connects to desktop TCP server
5
DEX Active Full desktop mode ready with bidirectional control

Quick Start for Developers

Get the project running on your local machine in a few steps.

Terminal
# Clone the repository
git clone https://github.com/Shrey113/Android-Dex.git
cd android_dex

# Install Flutter dependencies
flutter pub get

# Run the desktop app
flutter run -d windows

Prerequisites

  • Flutter SDK 3.7.0 or higher
  • Dart SDK (included with Flutter)
  • Windows/macOS/Linux development environment
  • ADB and scrcpy binaries in All helper/ folder

Project File Structure

Understanding the directory layout is key to navigating the codebase effectively.

lib/ Main Flutter source code
main.dart Application entry point
app.dart MaterialApp and theme configuration
screens/ UI screens
home_screen.dart Main UI with connection controls
services/ Core business logic
Socke_3_Android_Dex.dart TCP server and ADB commands
scrcpy_service.dart scrcpy process management
widgets/ Reusable UI components
glass_card.dart Glassmorphism card widget
All helper/ External binaries
platform-tools/ ADB executable
scrwin64/ scrcpy executable
mdns_service/ mDNS discovery service

Entry Point: main.dart

The application starts here. This file initializes the window manager, loads environment variables, starts the TCP server, and launches the Flutter app.

lib/main.dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await dotenv.load(fileName: ".env");

  // Desktop window configuration
  if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
    await windowManager.ensureInitialized();

    const windowOptions = WindowOptions(
      size: Size(480, 720),
      minimumSize: Size(400, 640),
      center: true,
      backgroundColor: Color(0xFF0D0D12),
      titleBarStyle: TitleBarStyle.hidden,
    );

    await windowManager.waitUntilReadyToShow(windowOptions, () async {
      await windowManager.show();
      await windowManager.focus();
    });
  }

  // Initialize TCP server for Android communication
  await AdbTcpServer.instance.init();

  runApp(const AndroidDexApp());
}
Key Responsibilities
  • Load environment variables from .env file
  • Configure custom window frame (frameless with custom title bar)
  • Start the singleton TCP server instance
  • Launch the Flutter MaterialApp

App Configuration: app.dart

This file defines the MaterialApp widget and the dark theme used throughout the application.

lib/app.dart
class AndroidDexApp extends StatelessWidget {
  const AndroidDexApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Android DEX',
      debugShowCheckedModeBanner: false,
      theme: _buildTheme(),
      home: const HomeScreen(),
    );
  }

  ThemeData _buildTheme() {
    const primaryColor = Color(0xFF7C4DFF);  // Purple accent
    const surfaceColor = Color(0xFF0D0D12); // Dark background
    const cardColor = Color(0xFF16161D);    // Card background
    // ... theme configuration
  }
}

Color Palette

Primary #7C4DFF
Secondary #00E5FF
Surface #0D0D12
Card #16161D
Success #1DB954
Error #E53935

TCP Server: AdbTcpServer

The heart of the communication system. This singleton class manages TCP connections with Android devices, processes commands, and controls device features.

lib/services/Socke_3_Android_Dex.dart

Singleton Pattern

Single instance accessible via AdbTcpServer.instance

Port 8456

Listens for incoming Android app connections

JSON Protocol

Line-delimited JSON messages for commands

Broadcast Support

Send messages to all connected clients

Socke_3_Android_Dex.dart - Server Initialization
class AdbTcpServer {
  static final AdbTcpServer instance = AdbTcpServer._internal();
  AdbTcpServer._internal();

  final int tcpPort = 8456;
  final Map<String, Socket> connectedClients = {};

  Future<bool> start() async {
    if (_isRunning) return true;
    try {
      _server = await ServerSocket.bind(InternetAddress.anyIPv4, tcpPort);
      _isRunning = true;
      _statusController.add(true);
      _listenForConnections();
      await _startMdnsService();
      return true;
    } catch (e) {
      return false;
    }
  }
}

Supported Commands

Command Response Type Description
getBluetooth getBluetooth Get current Bluetooth status
toggleBluetooth toggleBluetooth Toggle Bluetooth on/off
getMobileData getMobileData Get mobile data status
toggleMobileData toggleMobileData Toggle mobile data on/off
req_app_audio=pkg set_app_audio Route specific app audio to PC
stop_all_audio stop_all_audio Stop all audio streaming

Scrcpy Service

Manages the scrcpy process for display mirroring and virtual display creation.

lib/services/scrcpy_service.dart
scrcpy_service.dart - Launch Command
Future<(bool, String)> startScrcpy(String ip) async {
  _process = await Process.start(scrcpyFullPath, [
    '-s', ip,                           // Target device
    '--new-display=1920x1080/280',      // Virtual display
    '--no-audio',                       // Audio handled separately
    '--start-app=com.example.androiddex', // Launch DEX app
    '--no-vd-system-decorations',       // Clean desktop look
  ], workingDirectory: scrcpyDir);
  
  _isRunning = true;
  _statusController.add(true);
  return (true, 'Display started');
}
Important

The --new-display flag creates a virtual display on the Android device. This requires scrcpy version 2.4 or higher with virtual display support.

AdbDevice Class

Data model representing a connected Android device:

scrcpy_service.dart - AdbDevice Model
class AdbDevice {
  final String address;      // e.g., "192.168.1.100:5555"
  final String? product;     // e.g., "CPH2447"
  final String? model;       // e.g., "CPH2447"
  final String? device;      // e.g., "OP594DL1"
  final String? transportId; // ADB transport ID

  String get displayName => model ?? device ?? address;
}

mDNS Discovery Service

A Python-based service that broadcasts the desktop server's presence on the local network, allowing Android devices to auto-discover it.

ALL_PY/mdns_service.py
Python
from zeroconf import Zeroconf, ServiceInfo

SERVICE_TYPE = "_httpapp._tcp.local."
HTTP_PORT = 5001

def register_mdns_service(zeroconf):
    hostname, ip = get_network_info()
    service_name = f"{ip.replace('.', '_')}_win_server._httpapp._tcp.local."
    
    device_data = {
        "status": "success",
        "device_type": "windows",
        "device_info": {
            "hostname": hostname,
            "ip": ip,
        }
    }
    
    info = ServiceInfo(
        SERVICE_TYPE,
        service_name,
        addresses=[socket.inet_aton(ip)],
        port=HTTP_PORT,
        properties={
            b"host": hostname.encode(),
            b"device_type": b"windows",
            b"ip": ip.encode(),
        },
    )
    zeroconf.register_service(info)
How It Works

The mDNS service runs as a separate process, started by the Flutter app. It uses Zeroconf to broadcast a service named _httpapp._tcp.local. containing the desktop's IP address. Android apps on the same network can discover this service automatically.

Audio Process Manager

Manages per-app audio streaming from Android to the desktop using scrcpy's audio forwarding feature.

Socke_3_Android_Dex.dart - SimpleAudioProcessManager
class SimpleAudioProcessManager {
  static final SimpleAudioProcessManager instance = SimpleAudioProcessManager._();
  
  Process? _proc;
  String? _pkg;  // Currently streaming package name

  Future<bool> start(String pkg, List<String> args) async {
    if (_pkg == pkg) return true;  // Already streaming this app
    
    // Stop existing audio stream
    if (_proc != null) {
      _proc!.kill();
      _proc = null;
    }

    // Start new audio stream
    _proc = await Process.start(args[0], args.sublist(1));
    _pkg = pkg;
    return true;
  }
}

Home Screen UI

The main user interface containing the connection form, device list, and status indicators.

lib/screens/home_screen.dart

Component Structure

HomeScreen StatefulWidget
_buildBackground() Gradient + Grid
_buildTitleBar() Custom Window Bar
_buildLogo() Animated Logo
_buildConnectionPanel() GlassCard + TextField
_buildDeviceList() GlassCard + Device Cards

State Management

_isLoading Connection in progress
_isConnected Active DEX session
_connectedIp Current device IP
_devices List of discovered AdbDevice
_isServerRunning TCP server status

Glass Card Widget

A reusable glassmorphism card component with blur effect and subtle borders.

lib/widgets/glass_card.dart
glass_card.dart
class GlassCard extends StatelessWidget {
  final Widget child;
  final EdgeInsets padding;

  @override
  Widget build(BuildContext context) {
    return ClipRRect(
      borderRadius: BorderRadius.circular(16),
      child: BackdropFilter(
        filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
        child: Container(
          padding: padding,
          decoration: BoxDecoration(
            color: const Color(0xFF16161D).withValues(alpha: 0.8),
            borderRadius: BorderRadius.circular(16),
            border: Border.all(color: const Color(0xFF2A2A35)),
            boxShadow: [
              BoxShadow(
                color: Colors.black.withValues(alpha: 0.2),
                blurRadius: 16,
                offset: const Offset(0, 8),
              ),
            ],
          ),
          child: child,
        ),
      ),
    );
  }
}

Connection Flow Diagram

Visual representation of the complete connection process from user action to active DEX session.

User
HomeScreen
ScrcpyService
AdbTcpServer
Android
Enter IP / Select Device
connectAdb(ip)
adb connect ip:5555
connected
startScrcpy(ip)
scrcpy --new-display
TCP connect :8456
statusStream(true)
Update UI: Connected

Command Protocol

The TCP communication uses a simple line-delimited JSON protocol.

Request Format

// Simple command (string)
getBluetooth\n

// Command with parameter
req_app_audio=com.spotify.music\n

// JSON command
{"type":"add_new_recent_app","package":"com.example.app"}\n

Response Format

// All responses are JSON
{"type":"getBluetooth","status":true}\n
{"type":"set_app_audio","app":"com.spotify.music","status":true}\n
{"type":"toggleMobileData","status":false}\n

State Management

The app uses Dart's built-in StreamController for reactive state updates.

Stream-based State Pattern
// In Service class
final _statusController = StreamController<bool>.broadcast();
Stream<bool> get statusStream => _statusController.stream;

// Update state
_statusController.add(true);

// In UI (initState)
_serverStatusSubscription = AdbTcpServer.instance.statusStream.listen((isRunning) {
  if (mounted) {
    setState(() => _isServerRunning = isRunning);
  }
});
Benefits
  • Services remain decoupled from UI
  • Multiple listeners can subscribe
  • Automatic cleanup with subscription disposal