:feature:explore
August 15, 2026 · View on GitHub
Purpose
Owns Explore discovery and the Learn tab presentation: browse/search discovery, Learn curiosity cards, Learn history, and local UI state for those surfaces. It does not own recommendation engines, network clients, preference storage, playback services, or other feature screens.
Public API
ExploreScreenandExploreViewModelfor the Explore route.- Explore hero/browse headers use
rememberSectionHeaderFontFamily()from:core:designsystemfor section titles tied to Appearance lettering roundness; Explore and Learn use centralized Google Sans Flex weight tokens. Trending “All” uses the Top charts header with a leaderboard icon (genre filters still use “Top in {genre}”). - Explore list and selector-FAB clearance uses designsystem’s shared navigation-style / mini-player padding contract so controls remain above either app chrome.
- Trending genre row (
ExploreGenreSelector) uses sharedPillFilterChipicon+label pills (same language as onboarding search) with All + top genres + More opening the existing Browse Genres bottom sheet. - For You mood row (
ExploreVibeChipRow) sits in the same sticky header slot as genres (matched 8dp spacing). Soft 12dp capsules with per-mood icons + chromatic fill — distinct from stadium genre pills. Titles come from sharedCuratedMoods(same as Home daypart rails). Mood results and search idle “Suggested for you” titles share scrollableExploreIconTitleHeader(12dp top / 8dp bottom); suggestion blocks are icon + title only (no subtitle). - Explore search chips: Find a show (search icon; Meili typeahead + hybrid Also found, 300ms debounce) and Ask anything (sparkle icon; one CF embed → podcast + episode vectors via
GET /search/semantic, 1000ms debounce before embed; loader while waiting). Find-a-show pins the grid to the top via sharedProgressiveSearchScrollLogicwhen progressive catalog hits prepend over local substring matches or a Matches header appears; Ask anything is unchanged (one complete result set behind a loader). The chip row is a centered stadium (not full-width) sized for icon+label. Ask-anything idle/no-results: extra top inset, then centered title+subtitle and four full-width natural-question rows. Search-field placeholder follows the selected chip. Example queries were checked live against/search/semantic. Ask-anything results: Related shows rail + Episodes header above the hero/bento feed. LearnScreenandLearnViewModelfor the Learn route (curiosity cards, queue/play actions).LearnHistoryScreenandLearnHistoryViewModelfor Learn history.LearnCuriosityHistoryStorefor Learn history persistence through prefs APIs.LearnCuriosityCardas the feature UI model for curiosity cards.
Internal structure
src/main/java/cx/aswin/boxlore/feature/explore/
ExploreScreen.kt
ExploreViewModel.kt
LearnCuriosityCard.kt
LearnCuriosityHistoryStore.kt
LearnHistoryScreen.kt
LearnHistoryViewModel.kt
LearnScreen.kt
LearnViewModel.kt
components/
logic/
Dependencies
- Project dependencies:
:core:designsystem,:core:catalog,:core:playback,:core:model,:core:network,:core:analytics,:core:ranking, and:core:prefs. - Libraries: Compose, Navigation, Coil, lifecycle runtime/ViewModel Compose, Palette, kotlinx.serialization.json, Turbine and coroutines-test for tests.
- Reverse-edge rule: feature modules must not depend on other feature modules.
Threading / lifecycle
- ViewModels are scoped by app navigation.
- Catalog, playback, ranking, and prefs access come through injected application-scoped dependencies.
- Analytics: Explore/Learn ViewModels and screens call
:core:analyticsfaçades for glossary search / Learn exhaustion events (no PostHog direct). - UI runs on the main thread; search, recommendation, and history work use suspend APIs.
Persistence & identity
- This module owns no raw storage files.
- Learn history and recommendation caches are accessed through
BoxcastPrefsin:core:prefs. - Explore For You uses the same Home bootstrap recommendations payload: hydrate from
BoxcastPrefsshared cache, refresh viaPodcastRepository.getHomeBootstrapData(country + content languages), and write that cache back (taste or popular-in-region fallback). It does not call the standalone empty-seedgetPersonalizedRecommendationspath. - Explore vibe picks call
getCuratedPodcastswith the user's content region and languages (same as Home daypart rails). - Network DTOs map to feature UI models before entering UI state.
Testing notes
- Unit tests live under
feature/explore/src/test. - Existing coverage includes Learn pagination, Learn deck logic, Explore browse logic, and shared recommendation cache helpers (
ExploreSharedRecommendationsLogicTest). Find-a-show pin-to-top is covered in:core:designsystem(ProgressiveSearchScrollLogicTest). - Prefer fakes for repository and prefs dependencies when expanding ViewModel coverage.
./gradlew :feature:explore:testDebugUnitTest
CI relevance
unit-tests.ymlruns Explore JVM tests with the project suite.