A simple, offline-only Android application that displays UTC (Coordinated Universal Time) with support for phones, tablets and Android TVs.
- This is a single-module Android application built with Kotlin that provides an immersive fullscreen UTC time display.
- It features a hybrid UI approach combining traditional Android Views with Jetpack Compose components.
- Jetpack Navigation 3 is now stable and planned for integration, which will enable removal of Fragment use and transition to a pure Compose architecture.
- Universal Time Display: Clean, easy-to-read UTC clock
- Home Screen Widget: Resizable widget for quick UTC time access
- Note: Does not include anti-burn-in (image retention) prevention, as home screen display already exposes other static elements (icons, labels, etc.) to potential burn-in
- Multi-Platform Support:
- Android TV (leanback) optimized interface
- Mobile devices with touchscreen support
- Immersive Experience: Edge-to-edge fullscreen display with system UI hiding
- Modern Architecture: Built with latest Android development best practices
- Hybrid UI: Combines ViewBinding and Jetpack Compose for optimal flexibility
- Minimum SDK: Android 6.0 (API 23)
- Target SDK: Android 16 / Baklava (API 36)
- Java/Kotlin: JVM toolchain version 21
- Android Studio or IntelliJ IDEA with Android plugin
- JDK 21
- Android SDK with API level 36
- NDK 29.0.14206865
# Build the project
./gradlew build
# Build debug APK
./gradlew assembleDebug
# Build release APK (with R8 minification)
./gradlew assembleRelease
# Install debug build to connected device
./gradlew installDebugThe app uses modern Android architecture components:
- Navigation: Android Navigation Component with single Activity pattern
- Dependency Injection: Hilt (Dagger-based DI)
- UI: Hybrid approach with ViewBinding + Jetpack Compose
- Coroutines: Kotlin Coroutines for asynchronous operations
- Data Layer: Repository pattern for data management
app/src/main/java/com/dermochelys/utcclock/
├── Activity.kt # Main activity
├── Application.kt # Application class with Hilt setup
├── di/ # Dependency injection modules
│ ├── WidgetEntryPoint.kt # Hilt entry point for widget
│ ├── CoroutineModule.kt # Coroutine scope providers
│ ├── DataStoreModule.kt # DataStore providers
│ ├── DispatcherModule.kt # Coroutine dispatcher providers
│ └── RepositoryModule.kt # Repository providers
├── repository/ # Data layer (Repository pattern)
│ ├── DisclaimerRepository.kt # Disclaimer acceptance interface
│ ├── ZonedDateRepository.kt # UTC time data interface
│ └── internal/ # Repository implementations
│ ├── DisclaimerRepositoryImpl.kt
│ └── ZonedDateRepositoryImpl.kt
├── view/
│ ├── OverlayConst.kt # Shared overlay constants
│ ├── landing/ # Entry point fragment
│ ├── clock/ # Main UTC clock display
│ ├── disclaimer/ # Legal disclaimer view
│ ├── donation/ # Donation information dialog
│ ├── fontlicense/ # Font licensing dialog
│ └── common/ # Shared UI utilities
└── widget/ # Home screen widget (Glance-based)
├── GlanceAppWidget.kt # Widget implementation
├── GlanceAppWidgetReceiver.kt # Widget receiver
├── BroadcastReceiver.kt # Package replaced receiver
├── DisclaimerStateBroadcaster.kt # Disclaimer state sync
├── UpdateScheduler.kt # Alarm scheduling utilities
└── internal/ # Widget internal implementations
├── AutoFontSize.kt # Dynamic font sizing
├── BroadcastReceiverExt.kt # Receiver extensions
└── TextToBitmapRenderer.kt # Text rendering utilities
# Run lint analysis
./gradlew lint
# Lint vital checks for release
./gradlew lintVitalRelease# Check for dependency updates
./gradlew dependencyUpdatesDependencies are managed via gradle/libs.versions.toml using Gradle version catalogs.
# Run all unit tests (no emulator/device required)
./gradlew test
# Run instrumented tests (requires emulator/device)
./gradlew connectedAndroidTest- Unit Tests: Fast, isolated tests using MockK for mocking
- Instrumentation Tests: Integration tests with Hilt for full dependency injection testing
The project uses GitHub Actions for automated testing on every push and pull request to the main branch.
The CI workflow runs tests on two Android emulators using a matrix strategy:
- API 23 (minSdk) - Validates compatibility with the minimum supported Android version
- API 30 - Validates compatibility with an intermediate Android version
- API 36 (targetSdk) - Tests against the target Android version
CI emulators use:
- Google APIs system image
- x86_64 architecture
- Nexus 6 device profile
- Dependency Updates Check: Scans for available dependency updates
- Build & Test: Runs
./gradlew build connectedCheckon both API levels - Android ELF Alignment Check: Validates APK alignment
Note: When updating minSdk or targetSdk in app/build.gradle.kts, the matrix API levels in .github/workflows/android.yml must be manually updated to match.
- Current Version: 2.0.0+41
- Compile/Target SDK: 36 (Android 16 / Baklava)
For specific version numbers, see gradle/libs.versions.toml
Build Tools:
- Android Gradle Plugin
- Kotlin
- Kotlin Compose Compiler Plugin
- Kotlin Symbol Processing (KSP)
- Gradle
Core Libraries:
- AndroidX Core KTX
- AndroidX AppCompat
- Material Design
UI:
- Jetpack Compose (BOM)
- Compose Material3
- Activity Compose
- AndroidX TV Material
- AndroidX TV Foundation
Architecture:
- Hilt (Dependency Injection)
- AndroidX Navigation
- AndroidX Lifecycle (ViewModel, LiveData, Runtime)
- AndroidX DataStore Preferences
- AndroidX Fragment KTX
Widget:
- AndroidX Glance (App Widget & Material3)
Testing:
- JUnit 4
- MockK
- Kotlinx Coroutines Test
- AndroidX JUnit
- Espresso Core
- UI Automator
- R8 minification and resource shrinking enabled for release builds
- Java compiler warnings treated as errors
- Lint warnings treated as errors (with specific exceptions)