Bedrud التوثيق

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

TechnologyVersionPurpose
Kotlin2.1.0Language
Jetpack ComposeMaterial 3UI toolkit
Koin4.0.0Dependency injection
Retrofit + OkHttpLatestHTTP client
LiveKit Android SDK2.23.3WebRTC media
Credentials APILatestPasskey support
Encrypted SharedPreferencesLatestSecure storage
CoilLatestImage 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 */} DEPS

Key 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 ?: return

The ?: return pattern ensures the composable doesn’t render until the instance is fully initialized.

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

Product surface (what users get)

AreaBehavior
AuthLogin / register (and passkeys where supported)
DashboardRoom list; recent rooms after a join
Deep linksPasted / opened room URLs resolve to the correct room
MeetingJoin, mute, leave; usable controls chrome
Stage / shareStage overlay and screen-share path when the device allows
TelecomSelf-managed call connection; disconnect ends the meeting cleanly
RTLfa / ar locales with Shabnam — chrome should not clip

Shared product concepts (stage, chat, whiteboard on web) are documented under Using Bedrud. Feature parity with web may differ by release.

Deep Linking

The app handles URLs matching:

  • https://bedrud.com/m/* - Direct room join
  • https://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-android

Release Signing

Release builds require a keystore.properties file in the android project root with your signing configuration.