Skip to content

TV Client Application

KrugarValdes edited this page Jan 19, 2026 · 1 revision

TV Client Application

Purpose and Scope

This document covers the overall architecture, structure, and configuration of the Effective Office TV client application. This is a Compose Multiplatform application that serves as the primary interface for corporate information display on office televisions, allowing users to view employee stories, upcoming events, photo slideshows, and team statistics through automated content rotation with remote control navigation.

Technology Stack

Component Technology / Library Notes
Language / Runtime Kotlin 2.1.21, JVM 17 Aligns with shared Gradle toolchain
UI Framework Jetpack Compose 1.8.1 + Material 3 Multiplatform Compose with Material 3 components
Dependency Injection Koin 4.1.0 Dependency injection for feature screens
Networking Ktor 3.1.3 + kotlinx.serialization Fetches data from backend APIs
Image / Media Coil 3.2.0, ZXing 3.5.1 Image loading, QR code rendering, avatars
Navigation Decompose 3.3.0 Component-based navigation system
Concurrency Coroutines 1.10.2 + Flow Reactive streams and asynchronous processing
Build Tooling Gradle 8.x, Kotlin Compose Compiler 2.1.21 Project build and Compose compiler setup

Application Architecture

Overall Structure

overall-structure.svg

Navigation Overview

The TV application is built around an autoplay engine that continuously cycles through content categories. Each content type (Stories, Photos, Events) implements the AutoplayFeature contract, providing standardized lifecycle callbacks, navigation handling, and content rendering. The AutoplayComponent orchestrates transitions between screens, handles remote control intents (play/pause, next/previous),), and manages the overall playback state

Data Sources and Pipelines

Source Ingestion Path Purpose
Notion databases Ktor client + DTOs Birthdays, anniversaries, new hires
Duolingo API Ktor + serialization Weekly XP leaderboard and streak ("shock mode")
Leader ID API Ktor client Daily/monthly event promotion
Synology Photos API Ktor client Curated photo slideshow
Clockify API Ktor client Sport activity tracking and statistics

The data flows through a StoriesDataProvider that aggregates responses from multiple repositories, normalizes them into domain models, and emits them as Flow streams consumed by the UI components. Each pipeline includes error handling, local caching, and automatic refresh mechanisms to ensure content remains current without manual intervention

UI Flow and Navigation

  • AppActivity / RootComponent: Bootstraps Koin dependency injection, initializes the application environment, and routes between Welcome, Menu, and Autoplay surfaces using Decompose navigation. The navigation flow follows: Welcome → Menu (category selection) → Autoplay (slideshow)
  • Remote Control Layer: All focusable components expose focus states for D-pad navigation using FocusRequester and onFocusChanged callbacks. The playback controls are toggled via remote control buttons (Space/Center for pause/play, Left/Right for navigation, Escape/Back for exit)
  • Autoplay Manager: maintains queue definitions (e.g., Stories → Leader ID → Photos) stored in backend configuration or local overrides. Supports seamless crossfade transitions and per-screen duration settings.

Autoplay System Architecture

The autoplay system is the core innovation that enables seamless content rotation:

autoplay-system-architecture.svg

Module and Package Organization

Build Configuration

Key Android configuration extracted from tvApp/build.gradle.kts:

androidTarget()
jvm("desktop")
    
android {
    namespace = "band.effective.office.tv"
    compileSdk = 34
    defaultConfig {
        applicationId = "band.effective.office.tv"
        minSdk = 24
        targetSdk = 33
        buildConfigField("String", "backendApiUrl", localProperties["backendApiUrl"].toString())
        buildConfigField("String", "backendApiKey", localProperties["backendApiKey"].toString())
    }
    buildFeatures { compose = true }
}

Build config keys are injected from local.properties so the same artifact can target staging or production signage networks without code changes.

Dependency Structure

Module Category Modules Dependencies
Application composeApp All feature and core modules
Features feature:menu, feature:photos, feature:stories, feature:events Core modules, shared module
Core core:ui, core:domain, core:data External libraries, no cross-dependencies

Main Application Structure

RootComponent Responsibilities

The RootComponent serves as the application's primary coordinator, managing:

  • Navigation State: Stack navigation between Welcome, Menu, and Autoplay surfaces

  • Component Lifecycle: Creation and destruction of child components using Decompose

  • Initial Configuration: Routes to Welcome screen on first launch, then Menu for category selection

    val childStack: Value<ChildStack<*, Child>> = childStack(
    source = navigation,
    initialConfiguration = Config.Welcome,
    handleBackButton = true,
    childFactory = ::createChild,
    )

Feature Deep Dive

feature-architecture-overview.svg

  • Stories (Birthdays, Anniversaries, New Hires): Pulls employee attributes (name, avatar, congratulatory text) from Notion.
  • Duolingo Leaderboards: Aggregates XP totals and streak lengths, highlights daily winners, and animates their avatars.
  • Leader ID Events: Shows upcoming events, presenters, and an auto-generated QR linking to registration. Content refreshes daily.
  • Photo Slide Show: Streams Synology albums, supports album pinning and remote-triggered updates.
  • Autoplay: Operators can define ordered rotations, durations, and whether a screen is standalone or part of autoplay.

Configuration and Secrets

Add the following keys to local.properties (values provided via internal secrets storage):

Key Purpose
backendApiUrl Effective Office backend endpoint for configs
backendApiKey API key to fetch playlists, toggles, and shared assets

Additional credentials (Notion, Duolingo, Leader ID, Synology) are stored server-side; the TV app consumes proxied APIs to avoid storing sensitive secrets on devices.

Clone this wiki locally