127 lines
4.4 KiB
Markdown
127 lines
4.4 KiB
Markdown
# AGENTS.md
|
|||
|
|
|
||
|
|
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
|
||
|
|
|
||
|
|
## Project Overview
|
||
|
|
|
||
|
|
TeamSpeak Android native client with a two-layer architecture:
|
||
|
|
|
||
|
|
- **Protocol layer**: Go + [teamspeak-go](https://github.com/honeybbq/teamspeak-go) → compiled to `.aar` via gomobile
|
||
|
|
- **UI layer**: Kotlin + Jetpack Compose + Material Design 3
|
||
|
|
|
||
|
|
## Build Commands
|
||
|
|
|
||
|
|
### Full Build (Recommended)
|
||
|
|
```bash
|
||
|
|
# Windows
|
||
|
|
build.bat
|
||
|
|
|
||
|
|
# Linux/macOS
|
||
|
|
./build.sh
|
||
|
|
```
|
||
|
|
|
||
|
|
### Manual Build (Two Steps Required)
|
||
|
|
|
||
|
|
**Step 1: Compile Go → AAR**
|
||
|
|
```bash
|
||
|
|
cd go
|
||
|
|
set JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 # Windows only
|
||
|
|
gomobile bind -target=android -androidapi=26 -ldflags="-linkmode=external -extldflags=-Wl,--hash-style=both" -o ../android/app/libs/teamspeak.aar ./teamspeak
|
||
|
|
```
|
||
|
|
|
||
|
|
**Step 2: Build Android APK**
|
||
|
|
```bash
|
||
|
|
cd android
|
||
|
|
gradlew.bat assembleDebug # Windows
|
||
|
|
./gradlew assembleDebug # Linux/macOS
|
||
|
|
```
|
||
|
|
|
||
|
|
### Development Workflow
|
||
|
|
|
||
|
|
- **Modified Go code** (`go/`): Re-run Step 1, then run from IDE
|
||
|
|
- **Modified Kotlin code** (`android/`): Just run from IDE (IntelliJ/Android Studio)
|
||
|
|
|
||
|
|
Open the `android/` directory (not root) in IntelliJ IDEA or Android Studio.
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
### Go ↔ Kotlin Bridge
|
||
|
|
|
||
|
|
Communication flows through two bridge layers:
|
||
|
|
|
||
|
|
1. **Go side** (`go/teamspeak/bridge.go`): Exports `TSClient` class via gomobile
|
||
|
|
2. **Kotlin side** (`android/app/src/main/java/com/tsmobile/app/TSBridge.kt`): Wraps gomobile API
|
||
|
|
|
||
|
|
**Key constraints** (gomobile limitations):
|
||
|
|
- Cannot export `[]string`, `[]*T`, or Go `error` types
|
||
|
|
- Complex data passed as JSON strings (channels, clients)
|
||
|
|
- Events delivered via callback interfaces
|
||
|
|
- All JNI callbacks serialized through an event queue to avoid threading issues
|
||
|
|
|
||
|
|
**Event flow**: Go library → event queue → single consumer goroutine → JNI callback → Kotlin callback → ViewModel
|
||
|
|
|
||
|
|
### Android Layer Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
android/app/src/main/java/com/tsmobile/app/
|
||
|
|
├── MainActivity.kt # Entry point
|
||
|
|
├── TSBridge.kt # Go bridge wrapper
|
||
|
|
├── ui/
|
||
|
|
│ ├── components/ # Reusable UI components
|
||
|
|
│ ├── screens/ # Top-level screens
|
||
|
|
│ ├── navigation/NavGraph.kt # App navigation
|
||
|
|
│ └── theme/ # Material Design 3 theme
|
||
|
|
└── viewmodel/ # State management
|
||
|
|
├── ChannelViewModel.kt # Channel list & navigation
|
||
|
|
├── ChatViewModel.kt # Text messaging
|
||
|
|
├── ServerViewModel.kt # Connection & server info
|
||
|
|
└── VoiceViewModel.kt # Voice communication
|
||
|
|
```
|
||
|
|
|
||
|
|
State flows from ViewModels → Screens via `StateFlow`. User actions flow back via ViewModel methods.
|
||
|
|
|
||
|
|
## Critical Build Details
|
||
|
|
|
||
|
|
### Required Flags for gomobile
|
||
|
|
|
||
|
|
The `-ldflags` are **mandatory** to avoid runtime crashes:
|
||
|
|
|
||
|
|
1. **`-linkmode=external`**: Use NDK's external linker instead of Go's built-in linker
|
||
|
|
- **Why**: Prevents `SIGSEGV` crashes due to signal handling conflicts between Go runtime and Android
|
||
|
|
- Go uses SIGSEGV for GC and goroutine scheduling; Android's memory protection blocks this
|
||
|
|
|
||
|
|
2. **`-extldflags=-Wl,--hash-style=both`**: Generate compatible ELF hash tables
|
||
|
|
- **Why**: Go 1.24+ uses `DT_SUNW_HASH` by default; Android requires `DT_HASH` or `DT_GNU_HASH`
|
||
|
|
- Without this: `dlopen failed: empty/missing DT_HASH/DT_GNU_HASH` error
|
||
|
|
|
||
|
|
### Local Patches
|
||
|
|
|
||
|
|
The `go/_patches/github.com/honeybbq/teamspeak-go/` directory contains modified upstream code:
|
||
|
|
- Fixes 32-bit integer overflow issues (`math.MaxUint32` → platform-specific limits)
|
||
|
|
- Applied via `go.mod` replace directive: `replace github.com/honeybbq/teamspeak-go => ./_patches/github.com/honeybbq/teamspeak-go`
|
||
|
|
|
||
|
|
Do not remove this directory or the replace directive.
|
||
|
|
|
||
|
|
## Common Issues
|
||
|
|
|
||
|
|
### "javac: 非法字符" or "GBK unmappable character"
|
||
|
|
Set encoding before running gomobile on Windows:
|
||
|
|
```bash
|
||
|
|
set JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8
|
||
|
|
```
|
||
|
|
|
||
|
|
### App crashes with "SIGSEGV" on startup
|
||
|
|
Missing `-linkmode=external` flag. Rebuild AAR with correct flags.
|
||
|
|
|
||
|
|
### App crashes with "empty/missing DT_HASH"
|
||
|
|
Missing `-Wl,--hash-style=both` flag. Rebuild AAR with correct flags.
|
||
|
|
|
||
|
|
### "Unresolved reference: teamspeak"
|
||
|
|
AAR not compiled or not in `android/app/libs/teamspeak.aar`. Run Step 1 of manual build.
|
||
|
|
|
||
|
|
## Testing
|
||
|
|
|
||
|
|
Run from IDE with connected device or emulator. Check Logcat in IDE for runtime logs.
|
||
|
|
|
||
|
|
No automated test suite currently exists.
|