:core:model
September 7, 2026 · View on GitHub
Purpose
Owns shared domain models, enums, and pure value helpers used across network, data, playback, and UI modules. It does not own network DTOs, Room entities, Compose UI, repositories, or Android lifecycle behavior.
Public API
- Podcast, episode, briefing, chapter, person, transcript, and playback-adjacent model types.
EpisodeMediaCacheKey: Media3customCacheKeyhelper — briefing keys append audio URLv=so same-day regenerations bust the local audio cache.ContentRegion/ContentRegions: 11 chart storefronts, language allowlist/normalize/expand (id→id,in), off-market soft-warn helpers, and briefing market mapping that preserves the explicitglobalbriefing tab.ContentLanguageSelection: pure language-chip toggle rules (English lock, max languages, BCP47 normalization) for settings/onboarding pickers.PlaybackEntryPoint,ShareTarget, andShareLinkBuilder.PlaybackEntryPointcoarse values:GENERIC,HOME_MIXTAPE,LEARN,BRIEFING(Brief audio play synthesizes glossaryentry_point=briefingforplayback_*).AutoTranscriptState.PodcastGenresandRankingAggregateTelemetry.- Cross-promotion model types.
Podcast.isLatestEpisodeNew: shared NEW badge. RoomrssHasNewEpisodesis true for true-RSS freshness and for Podcast Index direct-feed tip promotions (updateLatestEpisode(..., markAsNew = true)). Opening the show clears the flag. Otherwise the 48h window / last-seen id rules apply.Podcast.effectiveGenre: resolves usercustomGenreoverride when non-blank, falling back to default cataloggenre. CompanioncustomGenreIconstores the icon identifier for custom tags.Podcast.recommendationGenre: resolves canonicalizedcustomGenreviaPodcastGenres.canonicalizeso valid standard reclassifications adapt personalized recommendations and Smart Queue, falling back togenrewhen the tag is arbitrary.FolderDisplaySize: display sizing enum (, , , , , , max) for library subscription folders, with placement properties (isPinnedToTop,placementLabel) distinguishing grid-movable () from top-pinned display sizes.SubscriptionFolder: domain model representing a custom folder organizing subscribed podcasts with optional icon, display size,showPodcastGridtoggle for 1×1 covers, and linked genre auto-syncing.
Internal structure
src/main/java/cx/aswin/boxlore/core/model/
Briefing.kt
Chapter.kt
CrossPromotion.kt
Episode.kt
EpisodeMediaCacheKey.kt
Podcast.kt
PodcastGenres.kt
RankingAggregateTelemetry.kt
ShareLinkBuilder.kt
SleepTimerConstants.kt
Transcript.kt
ContentRegion.kt
ContentLanguageSelection.kt
...
Dependencies
- Project dependencies: none.
- Libraries: Kotlinx serialization.
- Reverse-edge rule: Android framework, Room, Retrofit implementation details, repositories, and Compose UI must stay out of this module.
Threading / lifecycle
- Types are immutable or treated as immutable value data.
- No lifecycle owners, singletons, dispatchers, or background work are created here.
Persistence & identity
- This module owns no persistence files or storage keys.
- ID schemes carried by model fields are owned by their source modules, such as RSS podcast IDs and playback media IDs.
Testing notes
- Unit tests live under
core/model/src/test. ShareLinkBuilderTestcovers share URL invariants.- Prefer pure JVM tests for additional formatters or value helpers.
./gradlew :core:model:testDebugUnitTest
CI relevance
unit-tests.ymlcompiles this module for nearly every test target.- No module-specific CI job is required beyond JVM tests.