The Bedrud Android app is built with Jetpack Compose and Kotlin, providing a native video meeting experience with picture-in-picture, deep linking, and multi-instance support.
Technology Stack
| Technology | Version | Purpose |
|---|---|---|
| Kotlin | 2.1.0 | Language |
| Jetpack Compose | Material 3 | UI toolkit |
| Koin | 4.0.0 | Dependency injection |
| Retrofit + OkHttp | Latest | HTTP client |
| LiveKit Android SDK | 2.23.3 | WebRTC media |
| Credentials API | Latest | Passkey support |
| Encrypted SharedPreferences | Latest | Secure storage |
| Coil | Latest | Image loading |
Target: Min SDK 28, Target SDK 35, JDK 17
Directory Structure
apps/android/app/src/main/java/com/bedrud/app/
├── BedrudApplication.kt # Application class (Koin init)
├── MainActivity.kt # Single-activity entry point
├── core/
│ ├── api/ # Retrofit API client
│ │ ├── ApiClient.kt # Base HTTP client with auth interceptor
│ │ ├── AuthApi.kt # Auth endpoint definitions
│ │ └── RoomApi.kt # Room endpoint definitions
│ ├── auth/
│ │ └── AuthManager.kt # Token management, login/logout
│ ├── call/
│ │ ├── CallService.kt # Foreground service for calls
│ │ └── CallConnectionService.kt # Android ConnectionService
│ ├── deeplink/
│ │ └── DeepLinkHandler.kt # Handle bedrud.com deep links
│ ├── di/
│ │ └── AppModule.kt # Koin module definitions
│ ├── instance/
│ │ ├── InstanceManager.kt # Central multi-instance orchestrator
│ │ ├── InstanceStore.kt # Persistent instance storage
│ │ └── InstanceDeps.kt # Per-instance dependency container
│ ├── livekit/
│ │ └── RoomManager.kt # LiveKit room connection manager
│ └── pip/
│ └── PipManager.kt # Picture-in-Picture controller
├── models/
│ ├── User.kt # User data model
│ ├── Room.kt # Room data model
│ ├── Instance.kt # Server instance model
│ └── ApiResponse.kt # API response wrappers
└── ui/
├── screens/
│ ├── auth/
│ │ ├── LoginScreen.kt # Email/password + passkey login
│ │ └── RegisterScreen.kt # Account registration
│ ├── dashboard/
│ │ └── DashboardScreen.kt # Room list and management
│ ├── meeting/
│ │ └── MeetingScreen.kt # Video call interface
│ ├── instance/
│ │ ├── AddInstanceScreen.kt # Add server instance
│ │ └── InstanceSwitcher.kt # Switch between instances
│ ├── profile/
│ │ └── ProfileScreen.kt # User profile
│ └── settings/
│ └── SettingsScreen.kt # App settings
├── components/ # Reusable Compose components
└── theme/ # Material 3 theme definition
Multi-Instance Architecture
The Android app supports connecting to multiple Bedrud servers simultaneously.
flowchart TB
subgraph IM ["InstanceManager (Koin singleton)"]
IS["InstanceStore<br/>Persists list to SharedPreferences"]
AI["Active Instance (StateFlow)"]
INST["instances: List Instance"]
end
subgraph DEPS ["InstanceDeps"]
AM["AuthManager"]
AC["ApiClient"]
AA["AuthApi"]
RA["RoomApi"]
PM["PasskeyManager"]
RM["RoomManager"]
end
AI */} DEPSKey Pattern
All per-instance dependencies are exposed as StateFlow<T?> on InstanceManager. Composables collect them:
val authManager = instanceManager.authManager.collectAsState().value ?: return
val roomApi = instanceManager.roomApi.collectAsState().value ?: returnThe ?: return pattern ensures the composable doesn’t render until the instance is fully initialized.
Navigation Flow
flowchart LR
A["No instances"] */} B[AddInstanceScreen]
C["Has instances, not logged in"] */} D[LoginScreen]
E["Has instances, logged in"] */} F[DashboardScreen]The instance switcher appears as a ModalBottomSheet triggered from the Dashboard toolbar.
Features
Superficie de producto (lo que obtienen los usuarios)
| Área | Comportamiento |
|---|---|
| Auth | Login / registro (y passkeys donde se admitan) |
| Panel | Lista de salas; salas recientes tras unirse |
| Deep links | Las URL de sala pegadas / abiertas resuelven la sala correcta |
| Reunión | Unirse, silenciar, salir; chrome de controles usable |
| Escenario / compartir | Superposición de escenario y camino de compartir pantalla cuando el dispositivo lo permite |
| Telecom | Conexión de llamada autogestionada; desconectar termina la reunión de forma limpia |
| RTL | Locales fa / ar con Shabnam — el chrome no debería recortarse |
Los conceptos de producto compartidos (escenario, chat, pizarra en web) están documentados en Usar Bedrud. La paridad de funciones con web puede diferir por release.
Deep Linking
The app handles URLs matching:
https://bedrud.com/m/*- Direct room joinhttps://bedrud.com/c/*- Room join by code
Configured via intent filters in AndroidManifest.xml. Room URL parsing (BedrudURLParser and related) should open the correct room from a paste or external link.
Call Management
- CallService - Foreground service that keeps the connection alive during calls
- CallConnectionService - Integrates with Android’s telephony framework for proper call UI
- Required permissions:
MANAGE_OWN_CALLS,FOREGROUND_SERVICE_PHONE_CALL,FOREGROUND_SERVICE_CAMERA,FOREGROUND_SERVICE_MICROPHONE
Picture-in-Picture
The meeting screen supports PiP mode, allowing users to see the video feed while using other apps.
Passkeys
Uses Android’s Credentials API for FIDO2/WebAuthn passkey registration and login.
Building
# Debug APK
make build-android-debug
# Release APK (requires keystore.properties)
make build-android
# Build + install on connected device
make release-android
# Open in Android Studio
make dev-androidRelease Signing
Release builds require a keystore.properties file in the android project root with your signing configuration.