Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
.cxx
local.properties
keystore.properties
/.junie
151 changes: 151 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions .idea/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions .idea/markdown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/studiobot.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

104 changes: 104 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# BluetoothTerminalApp

This document provides an immediate, high-level understanding of the BluetoothTerminalApp codebase
for AI assistants, agents, and new developers.

## 1. Architecture Overview

The project follows **Clean Architecture** principles combined with **MVVM (
Model-ViewModel-ViewModel)** for the presentation layer. It is a single-module Android application (
`app` module) with internal layering and feature-based organization.

### Directory Structure

- `app/src/main/java/com/eva/bluetoothterminalapp/`
- `data/`: Implementations of domain repositories. Contains Android-specific Bluetooth and BLE
logic, DataStore persistence (Protobuf), and mappers.
- `domain/`: Business logic abstractions. Contains repository interfaces, domain models, enums,
and exceptions. This layer is independent of Android-specific implementation details where
possible.
- `di/`: Koin dependency injection modules (organized by functionality like `BLEModule`,
`BluetoothModule`, etc.).
- `presentation/`: UI logic and State management.
- Organized by features: `feature_connect`, `feature_devices`, `feature_le_connect`, etc.
- Each feature contains its own ViewModels, event contracts, and screen-level composables.
- `ui/`: Global UI theme (Color, Typography, Shape).

## 2. Core Tech Stack & Dependencies

- **UI:** Jetpack Compose (Material 3) with **Compose Destinations** for navigation.
- **Concurrency:** Kotlin Coroutines & Flow for asynchronous operations and reactive state.
- **Dependency Injection:** **Koin** (using `KoinStartup` for initialization).
- **Persistence:** **DataStore** with **Protobuf** serialization for app settings.
- **Bluetooth:** Standard Android Bluetooth APIs for Classic BT and Bluetooth Low Energy (BLE).
- **Serialization:** Kotlinx Serialization for JSON and Protobuf for DataStore.

## 3. Key Logic Hubs (The "Where to Look" Guide)

If you need to modify or understand core functionality, start here:

### Bluetooth Classic (BT)

- **Scanning:** `data/bluetooth/AndroidBluetoothScanner.kt`
- **Connection:** `data/bluetooth/AndroidBTClientConnector.kt` and `AndroidBTServerConnector.kt`
- **Data Transfer:** `data/bluetooth/BluetoothTransferService.kt`

### Bluetooth Low Energy (BLE)

- **Scanning:** `data/bluetooth_le/AndroidBluetoothLEScanner.kt`
- **Connection & GATT:** `data/bluetooth_le/AndroidBLEClientConnector.kt` and
`data/bluetooth_le/BLEClientGattCallback.kt`

### Settings & State

- **Persistence:** `data/datastore/` (Implementation) and `domain/settings/repository/` (
Interfaces).
- **Global Settings ViewModel:** `presentation/feature_settings/AppSettingsViewModel.kt`

### Navigation

- **Graph Definition:** `presentation/navigation/AppNavigation.kt`
- Uses **Compose Destinations** (look for `@Destination` annotations on composables).

## 4. Data Flow & State Management

The app follows a unidirectional data flow (UDF):

1. **User Action:** UI triggers an `Event` (e.g., `BTSettingsEvent`) in the `ViewModel`.
2. **ViewModel logic:** The `ViewModel` performs logic or calls a `Repository` method.
3. **Repository Action:** The `Repository` (implemented in `data/`) interacts with the Bluetooth
hardware or `DataStore`.
4. **State Update:** `DataStore` or Bluetooth status flows back as a `Flow`.
5. **UI Observation:** The `ViewModel` converts the `Flow` into a `StateFlow` (often using
`stateIn`). The Compose UI observes this state and recomposes.

## 5. AI/Agent Context & Guidelines

- **UI Development:** Always use **Jetpack Compose**. Follow the Material 3 design system. Design
tokens should be pulled from the `ui/theme` package.
- **Navigation:** Use **Compose Destinations**. Do not manually manage the NavGraph; use the
generated code and annotations.
- **Dependency Injection:** Use **Koin**. When adding new services or ViewModels, ensure they are
registered in the appropriate module in the `di/` package.
- **Immutability:** State exposed from ViewModels should be immutable. Use
`kotlinx-collections-immutable` where appropriate.
- **Permissions:** Bluetooth operations require runtime permissions. Ensure you check for
`android.permission.BLUETOOTH_CONNECT`, `BLUETOOTH_SCAN`, and `ACCESS_FINE_LOCATION` where
necessary.
- **Architecture Integrity:** Do not call Android Bluetooth APIs directly from ViewModels. Always go
through a domain-defined interface (Repository) and provide an implementation in the `data/`
layer.
- **Naming Conventions:** Repository implementations should be prefixed with `Android` if they are
platform-specific (e.g., `AndroidBluetoothScanner`).

## 6. Performance & Optimization (R8)

- **R8 Full Mode:** The project is configured to use **strict R8 Full Mode** (no compatibility
flags). This allows for aggressive constructor and member shrinking.
- **Rule Discipline:** Avoid adding broad keep rules (e.g., `-keep class com.package.** { *; }`).
Prefer narrow rules or relying on library consumer rules.
- **Protobuf:** Unused field shrinking for Protobuf is enabled via `-shrinkunusedprotofields` in
`proguard-rules.pro`.
- **Validation:** When upgrading libraries or adding reflection-heavy code, always verify
functionality in a `release` build variant to ensure R8 hasn't stripped required members.

14 changes: 5 additions & 9 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,18 @@ plugins {
alias(libs.plugins.kotlinx.serialization)
alias(libs.plugins.google.protobuf)
alias(libs.plugins.compose.compiler)
id("kotlin-parcelize")
}

android {
namespace = "com.eva.bluetoothterminalapp"
compileSdk = 36
compileSdk = libs.versions.android.compilesdk.get().toInt()

defaultConfig {
applicationId = "com.eva.bluetoothterminalapp"
minSdk = 29
targetSdk = 36
versionCode = 5
versionName = "1.2.1"
minSdk = libs.versions.android.minsdk.get().toInt()
targetSdk = libs.versions.android.targetsdk.get().toInt()
versionCode = 6
versionName = "1.2.2"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down Expand Up @@ -56,10 +55,8 @@ android {
buildTypes {

debug {
resValue("string", "app_name", "BluetoothTerminalApp (Debug)")
applicationIdSuffix = ".debug"
isMinifyEnabled = false
isShrinkResources = false
}

release {
Expand Down Expand Up @@ -114,7 +111,6 @@ dependencies {
//lifecycle compose runtime
implementation(libs.androidx.lifecycle.runtime.compose)
//navigation
implementation(libs.compose.destination.animation)
implementation(libs.compose.destination.core)
ksp(libs.compose.destination.ksp)
//kotlinx
Expand Down
4 changes: 4 additions & 0 deletions app/src/debug/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">BluetoothTerminalApp (Debug)</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,12 @@ class BLEServerGattCallback(
val characteristicDeferred = service.characteristics.map { characteristic ->
async { characteristic.toDomainModelWithNames(uuidReader) }
}
domainService.probableName = serviceName.await()?.name
domainService.characteristic = characteristicDeferred.awaitAll().toPersistentList()
val updatedService = domainService.copy(
probableName = serviceName.await()?.name,
characteristics = characteristicDeferred.awaitAll().toPersistentList()
)

_services.update { previous -> (previous + domainService).distinctBy { it.serviceId } }
_services.update { previous -> (previous + updatedService).distinctBy { it.serviceId } }

}.invokeOnCompletion {
// inform new service is added
Expand Down
Loading
Loading