Find free study rooms at Technische Hochschule Deggendorf — in real time.
THD Room Finder is a native Android app that helps students at Technische Hochschule Deggendorf (THD) instantly find available study rooms. It queries THD's public scheduling system THabella and cross-references occupied rooms against all known rooms to show which classrooms are free right now — or at any future time you choose.
No accounts. No backend. No configuration. Just open the app and find a room.
- Real-time free room finder — see which of THD's 289 rooms are available right now
- Building filter — quickly narrow results by building code (A, B, C, D, I, ITC, ...)
- Time-based lookup — pick any future date and time to check room availability ahead of schedule
- Room details — view capacity, equipment, floor, contact info, and the full day's schedule
- Offline support — network-first with local Room DB fallback (24h TTL for rooms, 5min for events)
- Auto-refresh — silent background polling every 5 minutes keeps data current
- Defensive parsing — handles unexpected API changes gracefully without crashing
- Material 3 with dynamic color support on Android 12+
- Dark mode follows system preference
- English UI with original German room and building names preserved
graph TB
subgraph UI["UI Layer — Jetpack Compose"]
Screens["Screens<br>(Home, RoomList, RoomDetail)"]
VMs["ViewModels"]
Components["Composable Components"]
end
subgraph Domain["Domain Layer"]
UseCases["Use Cases<br>(GetFreeRooms, GetRoomSchedule)"]
Models["Domain Models<br>(Room, FreeRoom, ScheduledEvent)"]
RepoIF["Repository Interfaces"]
end
subgraph Data["Data Layer"]
RepoImpl["Repository Implementations"]
Remote["Retrofit + OkHttp"]
Local["Room Database"]
end
API["THabella Public API<br>thabella.th-deg.de/thabella/opn/"]
Screens --> VMs
VMs --> UseCases
UseCases --> RepoIF
RepoImpl -.implements.-> RepoIF
RepoImpl --> Remote
RepoImpl --> Local
Remote --> API
style UI fill:#e3f2fd,stroke:#1565C0
style Domain fill:#e8f5e9,stroke:#2e7d32
style Data fill:#fff3e0,stroke:#e65100
Pattern: MVVM with Clean Architecture. Unidirectional data flow (UDF).
| Component | Technology |
|---|---|
| Language | Kotlin 2.2 |
| UI | Jetpack Compose + Material 3 |
| Navigation | Compose Navigation |
| Networking | Retrofit 2 + OkHttp |
| Serialization | kotlinx.serialization |
| Local DB | Room (caching + offline) |
| DI | Hilt (Dagger) |
| Async | Kotlin Coroutines + Flow |
| Build | Gradle (Kotlin DSL) + AGP 9 |
| Min SDK | 26 (Android 8.0) |
| Target SDK | 36 |
- Android Studio (latest stable, Ladybug or newer)
- JDK 21 — AGP 9 requires it. Android Studio bundles a compatible JDK.
- Android SDK with API level 36
# Clone the repository
git clone --recurse-submodules https://github.com/arudaev/THD-Room-Finder.git
cd THD-Room-Finder
# Build debug APK
./gradlew assembleDebug
# Run unit tests
./gradlew test
# Run lint checks
./gradlew lint
# Install on a connected device or emulator
./gradlew installDebugImportant
JAVA_HOME must point to JDK 21. If using Android Studio's bundled JDK:
# Windows
set JAVA_HOME=C:\Program Files\Android\Android Studio\jbr
# macOS / Linux
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"app/src/main/java/de/thd/roomfinder/
├── data/
│ ├── local/ # Room DB entities, DAOs, database
│ ├── mapper/ # DTO ↔ Domain mappers
│ ├── remote/ # Retrofit API service + DTOs
│ └── repository/ # Repository implementations
├── di/ # Hilt DI modules
├── domain/
│ ├── model/ # Room, FreeRoom, ScheduledEvent, Building
│ ├── repository/ # Repository interfaces
│ └── usecase/ # GetFreeRoomsUseCase, GetRoomScheduleUseCase
├── ui/
│ ├── component/ # RoomCard, ScheduleCard, BuildingFilterRow, ...
│ ├── navigation/ # NavHost + Route sealed class
│ ├── screen/ # HomeScreen, RoomListScreen, RoomDetailScreen
│ ├── theme/ # Material 3 colors, typography, theme
│ └── viewmodel/ # HomeViewModel, RoomListViewModel, RoomDetailViewModel
├── util/ # Constants
└── THDRoomFinderApp.kt # Hilt application class
Every push to main and every pull request triggers the CI workflow:
- Builds a debug APK
- Runs all unit tests
- Runs lint checks
Pushing a version tag triggers the Release workflow:
git tag v1.0.0
git push origin v1.0.0This will:
- Run the full test suite
- Build a signed release APK
- Create a GitHub Release with the APK attached and auto-generated release notes
Note
Required GitHub Secrets for signed releases:
| Secret | Description |
|---|---|
KEYSTORE_BASE64 |
Base64-encoded release keystore (base64 -w 0 release.keystore) |
KEYSTORE_PASSWORD |
Keystore password |
KEY_ALIAS |
Key alias name |
KEY_PASSWORD |
Key password |
Generate a keystore: keytool -genkey -v -keystore release.keystore -keyalg RSA -keysize 2048 -validity 10000 -alias release
The project includes 20+ unit tests covering:
| Layer | What's tested |
|---|---|
| Mappers | RoomMapper, PeriodMapper — DTO to domain model conversion |
| DTO parsing | JSON deserialization of RoomDto and PeriodDto |
| Use cases | GetFreeRoomsUseCase, GetRoomScheduleUseCase — business logic |
| ViewModels | HomeViewModel, RoomListViewModel, RoomDetailViewModel — state management |
Tests use fakes (not mocks) for the repository layer, with a FakeRoomRepository that provides controllable results and call tracking.
# Run all unit tests
./gradlew test
# Run with test report
./gradlew test --infoThe app communicates directly with THD's public scheduling system. No authentication is required.
| Endpoint | Method | Purpose |
|---|---|---|
/room/findRooms |
POST | Fetch all 289 rooms |
/period/findByDate/{dateTime} |
POST | Fetch events for a given date |
For full API documentation including request/response schemas, see the API Reference wiki page.
Warning
THabella has no official public API docs. These endpoints were reverse-engineered from its frontend source code and could change without notice.
Build fails with "invalid source release" or JDK errors
AGP 9 requires JDK 21. Make sure JAVA_HOME points to a JDK 21 installation, not JDK 8 or 17.
Windows: file lock errors on app/build/test-results/
Kill orphaned Gradle daemons and clear the locked directory:
taskkill /F /IM java.exe
rm -rf app/build/test-resultsHilt compilation fails with "BaseExtension not found"
Ensure Hilt version is 2.59.1 or higher. Earlier versions are not compatible with AGP 9.
FlowRow crashes at runtime
FlowRow has a known NoSuchMethodError with Compose BOM 2024.09.00 on AGP 9. The app avoids FlowRow and uses LazyRow instead.
Detailed documentation is available in the project wiki:
- Architecture — layers, data flow, caching strategy
- API Reference — THabella endpoints and schemas
- Building from Source — complete setup guide
- App Features — detailed feature documentation
- Contributing — how to contribute
Note
This application was developed as part of coursework at Technische Hochschule Deggendorf (THD) — Deggendorf Institute of Technology.
This project is licensed under the GNU General Public License v3.0 — see the LICENSE file for details.
Built for THD students who just need a quiet room to study.