tahamajs's picture
|
download
raw
18.9 kB

πŸ“± Android App: Clean MVVM Architecture

πŸ“ Recommended Project Structure

app/
β”œβ”€β”€ src/main/java/com/airmouse/
β”‚   β”œβ”€β”€ di/                           # Dependency Injection (Hilt)
β”‚   β”‚   β”œβ”€β”€ AppModule.kt
β”‚   β”‚   β”œβ”€β”€ NetworkModule.kt
β”‚   β”‚   └── SensorModule.kt
β”‚   β”œβ”€β”€ domain/                       # Clean Architecture: Domain Layer
β”‚   β”‚   β”œβ”€β”€ model/                    # Business models (pure Kotlin)
β”‚   β”‚   β”‚   β”œβ”€β”€ MouseEvent.kt
β”‚   β”‚   β”‚   β”œβ”€β”€ Gesture.kt
β”‚   β”‚   β”‚   └── ConnectionConfig.kt
β”‚   β”‚   β”œβ”€β”€ repository/               # Interfaces for data operations
β”‚   β”‚   β”‚   β”œβ”€β”€ IMouseRepository.kt
β”‚   β”‚   β”‚   β”œβ”€β”€ IGestureRepository.kt
β”‚   β”‚   β”‚   └── IConnectionRepository.kt
β”‚   β”‚   └── usecase/                  # Business logic (interactors)
β”‚   β”‚       β”œβ”€β”€ SendMovementUseCase.kt
β”‚   β”‚       β”œβ”€β”€ DetectGestureUseCase.kt
β”‚   β”‚       └── ConnectToServerUseCase.kt
β”‚   β”œβ”€β”€ data/                         # Data Layer
β”‚   β”‚   β”œβ”€β”€ repository/               # Implementations of repository interfaces
β”‚   β”‚   β”‚   β”œβ”€β”€ MouseRepositoryImpl.kt
β”‚   β”‚   β”‚   └── ConnectionRepositoryImpl.kt
β”‚   β”‚   β”œβ”€β”€ datasource/               # Local & remote data sources
β”‚   β”‚   β”‚   β”œβ”€β”€ local/                # Room database, DataStore
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ PreferencesDao.kt
β”‚   β”‚   β”‚   β”‚   └── AppDatabase.kt
β”‚   β”‚   β”‚   └── remote/               # WebSocket, Bluetooth, USB
β”‚   β”‚   β”‚       β”œβ”€β”€ WebSocketDataSource.kt
β”‚   β”‚   β”‚       β”œβ”€β”€ BluetoothDataSource.kt
β”‚   β”‚   β”‚       └── UsbDataSource.kt
β”‚   β”‚   └── model/                    # Data models (DTOs)
β”‚   β”‚       β”œβ”€β”€ NetworkMessage.kt
β”‚   β”‚       └── SensorData.kt
β”‚   β”œβ”€β”€ presentation/                 # Presentation Layer
β”‚   β”‚   β”œβ”€β”€ ui/                       # Jetpack Compose Screens
β”‚   β”‚   β”‚   β”œβ”€β”€ home/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ HomeScreen.kt
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ HomeViewModel.kt
β”‚   β”‚   β”‚   β”‚   └── HomeState.kt
β”‚   β”‚   β”‚   β”œβ”€β”€ calibration/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ CalibrationScreen.kt
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ CalibrationViewModel.kt
β”‚   β”‚   β”‚   β”‚   └── CalibrationState.kt
β”‚   β”‚   β”‚   β”œβ”€β”€ gesture/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ GestureStudioScreen.kt
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ GestureStudioViewModel.kt
β”‚   β”‚   β”‚   β”‚   └── GestureStudioState.kt
β”‚   β”‚   β”‚   β”œβ”€β”€ settings/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ SettingsScreen.kt
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ SettingsViewModel.kt
β”‚   β”‚   β”‚   β”‚   └── SettingsState.kt
β”‚   β”‚   β”‚   └── components/           # Reusable Compose components
β”‚   β”‚   β”‚       β”œβ”€β”€ ConnectionCard.kt
β”‚   β”‚   β”‚       β”œβ”€β”€ SensorDataCard.kt
β”‚   β”‚   β”‚       └── GestureChart.kt
β”‚   β”‚   β”œβ”€β”€ theme/                    # Material 3 theming
β”‚   β”‚   β”‚   β”œβ”€β”€ Color.kt
β”‚   β”‚   β”‚   β”œβ”€β”€ Theme.kt
β”‚   β”‚   β”‚   └── Type.kt
β”‚   β”‚   └── navigation/               # Compose Navigation
β”‚   β”‚       β”œβ”€β”€ NavGraph.kt
β”‚   β”‚       └── Destinations.kt
β”‚   β”œβ”€β”€ service/                      # Android Services
β”‚   β”‚   β”œβ”€β”€ SensorService.kt          # Foreground service for sensor collection
β”‚   β”‚   β”œβ”€β”€ GestureInferenceService.kt
β”‚   β”‚   β”œβ”€β”€ ProximityService.kt
β”‚   β”‚   β”œβ”€β”€ VoiceCommandService.kt
β”‚   β”‚   └── BluetoothHidService.kt
β”‚   β”œβ”€β”€ utils/                        # Utility classes
β”‚   β”‚   β”œβ”€β”€ Extensions.kt
β”‚   β”‚   β”œβ”€β”€ Constants.kt
β”‚   β”‚   └── MathUtils.kt
β”‚   └── AirMouseApplication.kt        # Application class
└── src/main/res/                     # Resources (icons, themes, etc.)

🧱 Core Principles

  1. Clean Architecture (Onion): The architecture follows concentric layers: UI β†’ Domain β†’ Data. Dependencies point inward, so the Domain layer (containing your business logic) remains independent of frameworks. For an Air Mouse app, this means the core logic for interpreting gestures and sending mouse commands is completely separate from Android‑specific code.

  2. MVVM with Jetpack Compose: The presentation layer uses ViewModel to hold UI state (exposed as StateFlow) and handle user events. The View (Compose UI) observes this state via collectAsState(). This ensures the UI only displays data and sends events, while the ViewModel contains the presentation logic.

  3. Repository Pattern: The repository interfaces in the Domain layer are implemented in the Data layer. This abstracts the source of data (network, Bluetooth, USB, local storage). For example, an IMovementRepository can have a WebSocketMovementDataSource and a BluetoothMovementDataSource, both interchangeable without changing the business logic.

  4. Dependency Injection (Hilt): All dependencies (repositories, use cases, data sources, services) are injected via Hilt. This makes the code testable and reduces boilerplate.

  5. Kotlin Flows for Asynchronous Data: Use StateFlow for UI state, SharedFlow for one‑time events (e.g., showToast), and Flow for continuous data streams (sensor data, incoming WebSocket messages). This integrates seamlessly with Compose.

πŸ”Œ Communication Protocols (Choice Matrix)

Select the protocol based on your specific requirements (Wi‑Fi reliability, latency tolerance, security needs, etc.):

Protocol Overhead Latency Pros Cons Best for
WebSocket (TCP) 2–14 bytes Low (stable) Reliable, ordered, firewall‑friendly, simple API Head‑of‑line blocking, TCP’s reliability can add jitter General use on good Wi‑Fi (your primary choice)
UDP + custom ACK 8 bytes Very low Lowest latency, no retransmission overhead Unreliable, packets may arrive out of order Gaming / ultra‑low‑latency requirements
QUIC Low (similar to TCP‑TLS) Lower than TCP Combines reliability and low latency, 0‑RTT handshake Emerging, library support varies Remote connections over the internet
USB HID <10 bytes Extremely low Direct cable, no network issues Wired only, requires OTG adapter Professional / stationary use
Bluetooth (HID/GATT) 5–10 bytes Low (5‑20 ms) Wireless, standardised HID profile Range limited, pairing required Casual / mobile use

For most cases, WebSocket over TCP is the best balance of simplicity and performance. If you later need to support weak networks, QUIC is a great upgrade because it avoids head‑of‑line blocking and allows 0‑RTT connection resumption. USB and Bluetooth HID modes are excellent for local, direct connections.

πŸ“‘ Message Protocol (Binary for Performance)

Strong recommendation: Use Protocol Buffers (protobuf) or MessagePack instead of JSON. These binary formats are significantly more compact and faster to parse, which is crucial for real‑time applications. Here is a minimal protobuf schema:

syntax = "proto3";

message Movement {
    float dx = 1;
    float dy = 2;
}

message Click {
    string button = 1;
}

message Scroll {
    int32 delta = 1;
}

message Command {
    oneof command_type {
        Movement move = 1;
        Click click = 2;
        DoubleClick double_click = 3;
        Scroll scroll = 4;
        Hello hello = 5;
    }
}

This reduces packet size by 30–50% compared to JSON, lowering latency and saving battery on the phone.

πŸ” Security & Data Persistence

  • Authentication: Use a short‑lived JWT token generated by the server and exchanged via a QR code. The token is included in the WebSocket upgrade request (ws://server:8080/ws?token=...). This prevents unauthorised clients from controlling the mouse.
  • Encryption: On the local network, you may skip TLS for performance, but for any remote connection you must use WSS (WebSocket over TLS) or QUIC (which has built‑in TLS 1.3).
  • Local Storage (Room): Store user preferences (sensitivity, thresholds), saved gestures (as templates), and calibration data in a local Room database.
  • DataStore: Use Preferences DataStore for simple key‑value settings (e.g., last used IP, theme).

πŸ€– Sensor Data Collection & Gesture Recognition

  • Foreground Service + SensorManager: Use a foreground service (with a persistent notification) to collect gyroscope and accelerometer data, even when the app is in the background. The service registers listeners with SensorManager at the fastest possible rate (SENSOR_DELAY_FASTEST).
  • Gesture Recognition Pipeline:
    1. Data Buffer: Maintain a sliding window of the last N sensor events (e.g., 50 samples).
    2. Feature Extraction: Compute angular velocity (gyro), linear acceleration, and orientation (using SensorManager.getRotationMatrixFromVector).
    3. Classification: Feed the features into a TensorFlow Lite model (trained on‑device) that outputs a gesture label and confidence.
    4. Post‑processing: Apply a confidence threshold (β‰₯0.7) and a cooldown (500 ms) to avoid duplicate triggers.
  • Optimisation: To reduce battery drain, lower the sensor sampling rate (SENSOR_DELAY_UI) when the phone is idle (detected via accelerometer variance).

πŸ’‘ Additional Considerations

  • Testing: Use the test source set with JUnit, MockK, and Robolectric. Mock external dependencies (WebSocket, Bluetooth) to test the use cases in isolation.
  • Error Handling: Use Either or a sealed class (Result<T>) to represent failures (network errors, sensor unavailable). Propagate these to the UI layer.
  • Multi‑Module Setup: Separate features into independent modules for better build times and team scalability: :core:domain, :core:data, :feature:home, :feature:calibration, etc.

The Android app is now designed to be scalable, testable, and maintainable while delivering a smooth real‑time mouse control experience.


πŸ–₯️ Go Server: Clean Architecture with Modular Design

πŸ“ Recommended Project Structure

airmouse-go/
β”œβ”€β”€ cmd/
β”‚   └── airmouse-server/
β”‚       └── main.go                 # Entry point, DI container
β”œβ”€β”€ internal/
β”‚   β”œβ”€β”€ domain/                     # Domain Layer (pure business logic)
β”‚   β”‚   β”œβ”€β”€ entity/                 # Core business objects
β”‚   β”‚   β”‚   β”œβ”€β”€ mouse.go
β”‚   β”‚   β”‚   β”œβ”€β”€ gesture.go
β”‚   β”‚   β”‚   └── client.go
β”‚   β”‚   β”œβ”€β”€ repository/             # Repository interfaces (for data access)
β”‚   β”‚   β”‚   β”œβ”€β”€ mouse_repository.go
β”‚   β”‚   β”‚   β”œβ”€β”€ gesture_repository.go
β”‚   β”‚   β”‚   └── client_repository.go
β”‚   β”‚   └── service/                # Business logic / Use Cases
β”‚   β”‚       β”œβ”€β”€ mouse_service.go
β”‚   β”‚       β”œβ”€β”€ gesture_service.go
β”‚   β”‚       └── connection_service.go
β”‚   β”œβ”€β”€ repository/                 # Data Layer
β”‚   β”‚   β”œβ”€β”€ mouse_repository_impl.go
β”‚   β”‚   β”œβ”€β”€ gesture_repository_impl.go
β”‚   β”‚   β”œβ”€β”€ client_repository_impl.go
β”‚   β”‚   └── config/                 # Configuration (JSON, env)
β”‚   β”‚       └── config.go
β”‚   β”œβ”€β”€ handler/                    # Delivery Layer (HTTP, WebSocket)
β”‚   β”‚   β”œβ”€β”€ websocket/
β”‚   β”‚   β”‚   β”œβ”€β”€ handler.go
β”‚   β”‚   β”‚   β”œβ”€β”€ client.go
β”‚   β”‚   β”‚   └── hub.go
β”‚   β”‚   β”œβ”€β”€ http/
β”‚   β”‚   β”‚   β”œβ”€β”€ router.go
β”‚   β”‚   β”‚   └── middleware.go
β”‚   β”‚   └── dto/                    # Data Transfer Objects
β”‚   β”‚       β”œβ”€β”€ message.go
β”‚   β”‚       └── response.go
β”‚   β”œβ”€β”€ infra/                      # Infrastructure (external dependencies)
β”‚   β”‚   β”œβ”€β”€ mouse/                  # Platform‑specific mouse control
β”‚   β”‚   β”‚   β”œβ”€β”€ mouse.go            # Interface
β”‚   β”‚   β”‚   β”œβ”€β”€ windows.go
β”‚   β”‚   β”‚   β”œβ”€β”€ darwin.go
β”‚   β”‚   β”‚   └── linux.go
β”‚   β”‚   β”œβ”€β”€ bluetooth/              # BLE, HID
β”‚   β”‚   β”‚   └── manager.go
β”‚   β”‚   β”œβ”€β”€ usb/                    # USB gadget mode
β”‚   β”‚   β”‚   └── gadget.go
β”‚   β”‚   └── logger/                 # Structured logging
β”‚   β”‚       └── logger.go
β”‚   └── pkg/                        # Shared utilities
β”‚       β”œβ”€β”€ errors/                 # Custom error types
β”‚       β”œβ”€β”€ utils/                  # Helper functions
β”‚       └── config/                 # Configuration loader
β”œβ”€β”€ pkg/                            # Public packages (if any)
β”œβ”€β”€ go.mod
β”œβ”€β”€ go.sum
└── Makefile

πŸ—οΈ Core Principles (Clean Architecture)

  • Layered Design: The architecture is divided into domain (business rules), repository (data access), handler (delivery), and infra (external dependencies). Dependencies point inward, so the domain layer knows nothing about WebSockets, databases, or OS‑specific mouse code.
  • Dependency Injection: In main.go, you manually wire all dependencies. The infra implementations are passed to the constructors of the repository and service layers. This makes the code testable (mock implementations can be substituted).
  • Concurrency: Use goroutines and channels for handling multiple clients. The WebSocket hub pattern (from gorilla/websocket) is perfect for broadcasting messages to all connected clients.
  • Configuration: Use a config.go that reads from a JSON file and environment variables. All config values are passed to the services at startup.

🧭 Communication Protocol Recommendations (Go Server)

The server is designed to support multiple protocols simultaneously, allowing the client to choose the best one. The recommended primary protocol is WebSocket over TCP because of its simplicity and reliability.

Protocol Library Use Case
WebSocket github.com/gorilla/websocket Primary protocol (Wi‑Fi, internet)
QUIC github.com/quic-go/quic-go Upgrade for low‑latency remote connections
UDP net Custom UDP + lightweight ACK
TCP net Plain TCP fallback
Bluetooth github.com/go-ble/ble BLE GATT service (discovery, not for data)
USB github.com/karalabe/hid USB gadget mode (emulate HID device)

You can implement a Transport interface and register all active transports in a central Hub. Each transport receives incoming messages, decodes them, and forwards them to the appropriate use case (e.g., MouseService.Move). Responses are sent back through the same transport.

πŸ“¦ Message Processing Pipeline

  1. WebSocket Handler: Upgrades HTTP connection, reads messages as binary (protobuf), and passes them to a MessageProcessor.
  2. MessageProcessor: Decodes the protobuf message and determines the type (move, click, hello). It then invokes the appropriate use case (e.g., mouseService.Move).
  3. Use Case (Service): Contains the business logic (e.g., apply sensitivity, filtering, gesture detection). It may call a repository (e.g., to store statistics).
  4. Repository: Accesses infrastructure (e.g., in‑memory storage for statistics, configuration).
  5. Response: The use case returns a result, which the MessageProcessor encodes back to protobuf and sends to the WebSocket client.

πŸ”§ Infrastructure Implementations

  • Mouse Control: Platform‑specific files (mouse_windows.go, mouse_darwin.go, mouse_linux.go) implement the Mouse interface using native APIs (Win32 API, CoreGraphics, uinput).
  • Bluetooth: The bluetooth package implements GATT services for discovery and pairing. It does not stream cursor data (use WebSocket for that).
  • USB Gadget Mode: The usb package uses linux/configfs (on supported devices) to emulate a USB HID mouse without any client software on the PC.
  • Logging: Structured logging with slog (Go 1.21+) or a third‑party library like zerolog. Each log entry includes a correlation ID to trace a request across multiple services.

πŸ€– AI and Personalization

  • ONNX Runtime: Use github.com/yalue/onnxruntime_go to load and execute the LSTM model for trajectory prediction. The inference runs in a separate goroutine (using a worker pool).
  • Data Collector: The personalization package collects movement samples (position, velocity, timestamp) and stores them in a circular buffer. When enough samples are collected, it triggers a HTTP call to a Python fine‑tuning service (running on localhost:5001). The updated model is then loaded via ONNX Runtime.

πŸ§ͺ Testing & Observability

  • Unit Tests: Write tests for each use case using the testing package and mocks for repositories.
  • Integration Tests: Test the WebSocket handler with a real client.
  • Metrics: Expose Prometheus metrics (requests per second, active connections, latency) on an /metrics endpoint.
  • Health Check: Provide a /health endpoint that returns 200 OK when the server is ready.

The Go server is now designed to be modular, highly performant, and horizontally scalable. Each layer is decoupled, allowing you to replace any component (e.g., switch from WebSocket to QUIC) without affecting the business logic. This architecture supports high‑concurrency (thousands of simultaneous clients) while maintaining sub‑20ms latency for cursor movement.


🀝 Cross-Project Synchronisation

To keep the Android app and Go server in sync, follow these guidelines:

  • Shared Protocol Buffer Schema: Maintain a single airmouse.proto file in a separate repository. Both projects generate code from this file (using the protobuf compiler). This ensures that message structures never become misaligned.
  • API Versioning: Prefix your WebSocket endpoint with a version, e.g., /ws/v1/. This allows you to introduce breaking changes later.
  • Continuous Integration: Set up a CI pipeline that, on any change to the .proto file, automatically regenerates the code for both projects and runs the full test suite.
  • Version Tags: Tag releases with semantic versioning (e.g., v1.2.3) and update the Android app’s build script to require a minimum server version.

Xet Storage Details

Size:
18.9 kB
Β·
Xet hash:
bf912e8367164748ad46007f1b71023b83f55c31a748e0177c46cd08d7bf025c

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.