├── .editorconfig ├── .gitattributes ├── .github ├── scripts │ └── mac-disk-cleanup.sh └── workflows │ ├── baseline-profile.yml │ ├── build.yml │ ├── gradle-wrapper.yaml │ └── todo.yml ├── .gitignore ├── .idea ├── checkstyle-idea.xml ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── copyright │ ├── Chris_Banes_Apache_v2.xml │ ├── Google_Apache_v2_0.xml │ └── profiles_settings.xml ├── dictionaries │ ├── chris.xml │ └── chrisbanes.xml ├── encodings.xml ├── icon.png ├── inspectionProfiles │ ├── ktlint.xml │ └── profiles_settings.xml ├── kotlinc.xml └── vcs.xml ├── .ruby-version ├── .swiftlint.yml ├── .xcode-version ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── android-app ├── app │ ├── benchmark-rules.pro │ ├── build.gradle.kts │ ├── proguard-rules-chucker.pro │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ ├── screenshots │ │ │ └── Screenshots.kt │ │ │ └── test │ │ │ └── smoke │ │ │ └── SmokeTest.kt │ │ ├── debug │ │ ├── AndroidManifest.xml │ │ └── res │ │ │ └── values │ │ │ ├── colors.xml │ │ │ └── strings.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── generated │ │ │ └── baselineProfiles │ │ │ │ ├── baseline-prof.txt │ │ │ │ └── startup-prof.txt │ │ ├── kotlin │ │ │ └── app │ │ │ │ └── tivi │ │ │ │ ├── TiviActivity.kt │ │ │ │ ├── TiviApplication.kt │ │ │ │ └── home │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-anydpi-v26 │ │ │ ├── ic_launcher_background.xml │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ └── ic_launcher.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.webp │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.webp │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.webp │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.webp │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.webp │ │ │ ├── values-night │ │ │ ├── colors.xml │ │ │ ├── sys_ui.xml │ │ │ └── themes.xml │ │ │ ├── values-notnight-v27 │ │ │ └── sys_ui.xml │ │ │ ├── values-v29 │ │ │ └── themes.xml │ │ │ ├── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ ├── sys_ui.xml │ │ │ └── themes.xml │ │ │ └── xml │ │ │ ├── backup_rules.xml │ │ │ └── data_extraction_rules.xml │ │ └── qa │ │ └── res │ │ └── values │ │ └── leak_canary.xml ├── benchmark │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── benchmark │ │ ├── BaselineProfileGenerator.kt │ │ ├── StartupBenchmark.kt │ │ └── Utils.kt └── common-test │ ├── build.gradle.kts │ └── src │ └── main │ └── kotlin │ └── app │ └── tivi │ └── app │ └── test │ └── AppScenarios.kt ├── api ├── tmdb │ ├── build.gradle.kts │ └── src │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── tmdb │ │ │ ├── TmdbComponent.kt │ │ │ ├── TmdbImageSizes.kt │ │ │ ├── TmdbImageUrlProvider.kt │ │ │ ├── TmdbInitializer.kt │ │ │ ├── TmdbManager.kt │ │ │ └── TmdbOAuthInfo.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── tmdb │ │ │ └── TmdbPlatformComponent.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── tmdb │ │ └── TmdbPlatformComponent.kt └── trakt │ ├── build.gradle.kts │ └── src │ ├── commonMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── trakt │ │ ├── Anticipated.kt │ │ ├── TiviTrakt.kt │ │ └── TraktComponent.kt │ ├── iosMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── trakt │ │ └── TraktPlatformComponent.kt │ └── jvmMain │ └── kotlin │ └── app │ └── tivi │ └── trakt │ └── TraktPlatformComponent.kt ├── art ├── account.png ├── banner.png ├── episode-details.gif ├── show-details.gif ├── theme-baseline.sketch └── tivi-art.sketch ├── build.gradle.kts ├── common ├── imageloading │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── common │ │ │ └── imageloading │ │ │ └── ImageLoadingPlatformComponent.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── common │ │ │ └── imageloading │ │ │ ├── EpisodeImageModelInterceptor.kt │ │ │ ├── HideArtworkInterceptor.kt │ │ │ ├── ImageLoader.kt │ │ │ ├── ImageLoaderCleanupInitializer.kt │ │ │ ├── ImageLoadingComponent.kt │ │ │ ├── SeasonImageModelInterceptor.kt │ │ │ ├── ShowImageModelInterceptor.kt │ │ │ └── TmdbImageEntityCoilInterceptor.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── common │ │ │ └── imageloading │ │ │ └── ImageLoadingPlatformComponent.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── common │ │ └── imageloading │ │ └── ImageLoadingPlatformComponent.kt └── ui │ ├── circuit │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ ├── EventSink.kt │ │ ├── navigation │ │ ├── DeepLinker.kt │ │ └── LocalNavigator.kt │ │ └── overlays │ │ ├── BottomSheetOverlay.kt │ │ └── DialogOverlay.kt │ ├── compose │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── common │ │ │ └── compose │ │ │ ├── Coil.kt │ │ │ ├── ColorExtractor.android.kt │ │ │ ├── ReportDrawnWhen.kt │ │ │ ├── theme │ │ │ └── Platform.kt │ │ │ └── ui │ │ │ └── Icon.android.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── common │ │ │ └── compose │ │ │ ├── ColorExtractor.kt │ │ │ ├── EntryGrid.kt │ │ │ ├── HazeScaffold.kt │ │ │ ├── Layout.kt │ │ │ ├── LazyList.kt │ │ │ ├── LazyPagingExtensions.kt │ │ │ ├── LogCompositions.kt │ │ │ ├── Modifier.kt │ │ │ ├── NestedScaffold.kt │ │ │ ├── ReportDrawnWhen.kt │ │ │ ├── RetainedCoroutineScope.kt │ │ │ ├── TiviCompositionLocals.kt │ │ │ ├── TiviPreferenceExtensions.kt │ │ │ ├── UiMessage.kt │ │ │ ├── WindowSizeClass.kt │ │ │ ├── theme │ │ │ ├── Color.kt │ │ │ ├── Platform.kt │ │ │ ├── Shape.kt │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ │ │ └── ui │ │ │ ├── AppBar.kt │ │ │ ├── AutoSizedCircularProgressIndicator.kt │ │ │ ├── Backdrop.kt │ │ │ ├── BackdropCard.kt │ │ │ ├── Clickable.kt │ │ │ ├── DateTimeDialogs.kt │ │ │ ├── DateTimeTextFields.kt │ │ │ ├── Empty.kt │ │ │ ├── ExpandingSummary.kt │ │ │ ├── GradientScrim.kt │ │ │ ├── Icon.kt │ │ │ ├── IconButtonScrim.kt │ │ │ ├── Image.kt │ │ │ ├── LoadingButton.kt │ │ │ ├── PaddingValues.kt │ │ │ ├── Position.kt │ │ │ ├── PosterCard.kt │ │ │ ├── Preference.kt │ │ │ ├── RefreshButton.kt │ │ │ ├── SearchTextField.kt │ │ │ ├── SortChip.kt │ │ │ ├── SortMenuPopup.kt │ │ │ ├── Surface.kt │ │ │ ├── UserProfileButton.kt │ │ │ └── WindowInsets.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── common │ │ │ └── compose │ │ │ ├── Coil.kt │ │ │ ├── ColorExtractor.ios.kt │ │ │ ├── ReportDrawnWhen.kt │ │ │ ├── theme │ │ │ └── Platform.kt │ │ │ └── ui │ │ │ ├── DateTimeDialogs.kt │ │ │ ├── Icon.ios.kt │ │ │ └── Sheets.kt │ │ ├── jvmCommon │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── common │ │ │ └── compose │ │ │ └── ui │ │ │ └── DateTimeDialogs.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── common │ │ └── compose │ │ ├── Coil.kt │ │ ├── ColorExtractor.jvm.kt │ │ ├── ReportDrawnWhen.kt │ │ ├── theme │ │ └── Platform.kt │ │ └── ui │ │ └── Icon.jvm.kt │ ├── resources │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── util │ │ │ ├── String.kt │ │ │ ├── TiviDateFormatter.kt │ │ │ └── TiviTextCreator.kt │ │ ├── commonMain │ │ ├── composeResources │ │ │ ├── font │ │ │ │ ├── dm_sans_bold.ttf │ │ │ │ ├── dm_sans_medium.ttf │ │ │ │ └── dm_sans_regular.ttf │ │ │ └── values │ │ │ │ └── strings.xml │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ ├── common │ │ │ └── ui │ │ │ │ └── resources │ │ │ │ ├── Fonts.kt │ │ │ │ └── Resources.kt │ │ │ └── util │ │ │ ├── GenreStringer.kt │ │ │ ├── String.kt │ │ │ ├── TiviDateFormatter.kt │ │ │ └── TiviTextCreator.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── util │ │ │ ├── String.kt │ │ │ ├── TiviDateFormatter.kt │ │ │ └── TiviTextCreator.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── util │ │ ├── String.kt │ │ ├── TiviDateFormatter.kt │ │ └── TiviTextCreator.kt │ └── screens │ ├── build.gradle.kts │ └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── screens │ ├── Parcelize.kt │ └── Screens.kt ├── compose-stability.conf ├── core ├── analytics │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── core │ │ │ └── analytics │ │ │ ├── AnalyticsPlatformComponent.kt │ │ │ └── TiviFirebaseAnalytics.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── core │ │ │ └── analytics │ │ │ ├── Analytics.kt │ │ │ ├── AnalyticsComponent.kt │ │ │ └── AnalyticsInitializer.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── core │ │ │ └── analytics │ │ │ └── AnalyticsPlatformComponent.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── core │ │ └── analytics │ │ └── AnalyticsPlatformComponent.kt ├── base │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ ├── animations │ │ └── Lerp.kt │ │ ├── app │ │ └── ApplicationInfo.kt │ │ ├── appinitializers │ │ └── AppInitializer.kt │ │ ├── base │ │ └── InvokeStatus.kt │ │ ├── extensions │ │ └── Lazy.kt │ │ ├── inject │ │ └── Scopes.kt │ │ └── util │ │ ├── AppCoroutineDispatchers.kt │ │ ├── Collections.kt │ │ ├── CoroutineContext.kt │ │ └── Result.kt ├── logging │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── util │ │ │ ├── AndroidSetCrashReportingEnabledAction.kt │ │ │ ├── CrashlyticsAndroidInitializer.kt │ │ │ └── LoggerPlatformComponent.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── util │ │ │ ├── CrashReportingInitializer.kt │ │ │ ├── KermitInitializer.kt │ │ │ ├── LoggerComponent.kt │ │ │ ├── RecordingLoggerWriter.kt │ │ │ └── SetCrashReportingEnabledAction.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── util │ │ │ ├── CrashlyticsIosInitializer.kt │ │ │ └── LoggerPlatformComponent.kt │ │ ├── jvmMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── util │ │ │ └── LoggerPlatformComponent.kt │ │ └── mobileMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── util │ │ └── CrashlyticsLoggerWriter.kt ├── notifications │ ├── core │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── androidMain │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── app │ │ │ │ │ └── tivi │ │ │ │ │ └── core │ │ │ │ │ └── notifications │ │ │ │ │ ├── AndroidNotificationManager.kt │ │ │ │ │ ├── NotificationPlatformComponent.kt │ │ │ │ │ ├── PendingNotificationStore.kt │ │ │ │ │ └── PostNotificationBroadcastReceiver.kt │ │ │ └── res │ │ │ │ └── drawable │ │ │ │ └── outline_tv_gen_24.xml │ │ │ ├── commonMain │ │ │ └── kotlin │ │ │ │ └── app │ │ │ │ └── tivi │ │ │ │ └── core │ │ │ │ └── notifications │ │ │ │ ├── NotificationManager.kt │ │ │ │ └── NotificationsComponent.kt │ │ │ ├── iosMain │ │ │ └── kotlin │ │ │ │ └── app │ │ │ │ └── tivi │ │ │ │ └── core │ │ │ │ └── notifications │ │ │ │ ├── IosNotificationManager.kt │ │ │ │ └── NotificationPlatformComponent.kt │ │ │ └── jvmMain │ │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── core │ │ │ └── notifications │ │ │ ├── EmptyNotificationManager.kt │ │ │ └── NotificationPlatformComponent.kt │ └── protos │ │ ├── build.gradle.kts │ │ └── src │ │ └── commonMain │ │ └── proto │ │ └── pending.proto ├── performance │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── core │ │ │ └── perf │ │ │ ├── AndroidTracer.kt │ │ │ └── PerformanceComponent.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── core │ │ │ └── perf │ │ │ ├── PerformanceComponent.kt │ │ │ └── Tracer.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── core │ │ │ └── perf │ │ │ └── PerformanceComponent.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── core │ │ └── perf │ │ └── PerformanceComponent.kt ├── permissions │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── core │ │ │ └── permissions │ │ │ ├── PermissionsController.android.kt │ │ │ └── PermissionsPlatformComponent.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── core │ │ │ └── permissions │ │ │ ├── PermissionsComponent.kt │ │ │ └── PermissionsController.common.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── core │ │ │ └── permissions │ │ │ └── PermissionsPlatformComponent.kt │ │ ├── jvmMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── core │ │ │ └── permissions │ │ │ └── PermissionsPlatformComponent.kt │ │ └── mokoImplMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── core │ │ └── permissions │ │ └── PermissionsController.ios.kt ├── powercontroller │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── util │ │ │ ├── AndroidPowerController.kt │ │ │ └── PowerControllerComponent.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── util │ │ │ ├── PowerController.kt │ │ │ └── PowerControllerComponent.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── util │ │ │ └── PowerControllerComponent.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── util │ │ └── PowerControllerComponent.kt └── preferences │ ├── build.gradle.kts │ └── src │ ├── androidMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── settings │ │ └── PreferencesComponent.kt │ ├── commonMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── settings │ │ ├── Preference.kt │ │ ├── PreferencesComponent.kt │ │ ├── TiviPreferences.kt │ │ └── TiviPreferencesImpl.kt │ ├── iosMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── settings │ │ └── PreferencesPlatformComponent.kt │ └── jvmMain │ └── kotlin │ └── app │ └── tivi │ └── settings │ └── PreferencesPlatformComponent.kt ├── data ├── anticipatedshows │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── anticipatedshows │ │ ├── AnticipatedShowsBinds.kt │ │ ├── AnticipatedShowsDataSource.kt │ │ ├── AnticipatedShowsLastRequestStore.kt │ │ ├── AnticipatedShowsStore.kt │ │ └── TraktAnticipatedShowsDataSource.kt ├── db-sqldelight │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── data │ │ │ └── SqlDelightDatabasePlatformComponent.kt │ │ ├── commonMain │ │ ├── kotlin │ │ │ └── app │ │ │ │ └── tivi │ │ │ │ └── data │ │ │ │ ├── DatabaseFactory.kt │ │ │ │ ├── SqlDelightDatabaseComponent.kt │ │ │ │ ├── SqlDelightTransactionRunner.kt │ │ │ │ ├── Utils.kt │ │ │ │ ├── columnadaptors │ │ │ │ ├── DayOfWeekColumnAdapter.kt │ │ │ │ ├── ImageTypeColumnAdapter.kt │ │ │ │ ├── InstantLongColumnAdapter.kt │ │ │ │ ├── InstantStringColumnAdapter.kt │ │ │ │ ├── LocalDateColumnAdapter.kt │ │ │ │ ├── LocalDateTimeColumnAdapter.kt │ │ │ │ ├── LocalTimeColumnAdapter.kt │ │ │ │ ├── PendingActionColumnAdapter.kt │ │ │ │ ├── RequestColumnAdapter.kt │ │ │ │ ├── ShowStatusColumnAdapter.kt │ │ │ │ └── TimeZoneColumnAdapter.kt │ │ │ │ ├── daos │ │ │ │ ├── SqlDelightAnticipatedShowsDao.kt │ │ │ │ ├── SqlDelightEntityDao.kt │ │ │ │ ├── SqlDelightEpisodeWatchEntryDao.kt │ │ │ │ ├── SqlDelightEpisodesDao.kt │ │ │ │ ├── SqlDelightFollowedShowsDao.kt │ │ │ │ ├── SqlDelightLastRequestDao.kt │ │ │ │ ├── SqlDelightLibraryShowsDao.kt │ │ │ │ ├── SqlDelightPopularShowsDao.kt │ │ │ │ ├── SqlDelightRecommendedShowsDao.kt │ │ │ │ ├── SqlDelightRelatedShowsDao.kt │ │ │ │ ├── SqlDelightSeasonsDao.kt │ │ │ │ ├── SqlDelightShowImagesDao.kt │ │ │ │ ├── SqlDelightTiviShowDao.kt │ │ │ │ ├── SqlDelightTrendingShowsDao.kt │ │ │ │ ├── SqlDelightUserDao.kt │ │ │ │ └── SqlDelightWatchedShowsDao.kt │ │ │ │ └── paging │ │ │ │ ├── KeyedQueryPagingSource.kt │ │ │ │ ├── OffsetQueryPagingSource.kt │ │ │ │ └── QueryPagingSource.kt │ │ └── sqldelight │ │ │ ├── app │ │ │ └── tivi │ │ │ │ └── data │ │ │ │ ├── anticipated_shows.sq │ │ │ │ ├── episode_watch_entries.sq │ │ │ │ ├── episodes.sq │ │ │ │ ├── last_requests.sq │ │ │ │ ├── library_shows.sq │ │ │ │ ├── myshows_entries.sq │ │ │ │ ├── popular_shows.sq │ │ │ │ ├── recommended_entries.sq │ │ │ │ ├── related_shows.sq │ │ │ │ ├── seasons.sq │ │ │ │ ├── show.sq │ │ │ │ ├── show_images.sq │ │ │ │ ├── shows_last_watched.sq │ │ │ │ ├── shows_next_to_watch.sq │ │ │ │ ├── shows_view_watch_stats.sq │ │ │ │ ├── trending_shows.sq │ │ │ │ ├── upnext_shows.sq │ │ │ │ ├── users.sq │ │ │ │ └── watched_entries.sq │ │ │ └── migrations │ │ │ ├── 29.sqm │ │ │ ├── 30.sqm │ │ │ ├── 31.sqm │ │ │ ├── 32.sqm │ │ │ ├── 33.sqm │ │ │ └── 34.sqm │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── data │ │ │ └── SqlDelightDatabasePlatformComponent.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── SqlDelightDatabasePlatformComponent.kt ├── db │ ├── build.gradle.kts │ ├── room-schemas │ │ ├── 1.json │ │ ├── 10.json │ │ ├── 11.json │ │ ├── 12.json │ │ ├── 13.json │ │ ├── 14.json │ │ ├── 15.json │ │ ├── 16.json │ │ ├── 17.json │ │ ├── 18.json │ │ ├── 19.json │ │ ├── 2.json │ │ ├── 20.json │ │ ├── 21.json │ │ ├── 22.json │ │ ├── 23.json │ │ ├── 24.json │ │ ├── 25.json │ │ ├── 26.json │ │ ├── 27.json │ │ ├── 28.json │ │ ├── 29.json │ │ ├── 3.json │ │ ├── 30.json │ │ ├── 31.json │ │ ├── 32.json │ │ ├── 4.json │ │ ├── 5.json │ │ ├── 6.json │ │ ├── 7.json │ │ ├── 8.json │ │ └── 9.json │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ ├── daos │ │ ├── AnticipatedShowsDao.kt │ │ ├── EntityDao.kt │ │ ├── EntryDao.kt │ │ ├── EpisodeWatchEntryDao.kt │ │ ├── EpisodesDao.kt │ │ ├── FollowedShowsDao.kt │ │ ├── LastRequestDao.kt │ │ ├── LibraryShowsDao.kt │ │ ├── PaginatedEntryDao.kt │ │ ├── PairEntryDao.kt │ │ ├── PopularDao.kt │ │ ├── RecommendedDao.kt │ │ ├── RelatedShowsDao.kt │ │ ├── SeasonsDao.kt │ │ ├── ShowFtsDao.kt │ │ ├── ShowTmdbImagesDao.kt │ │ ├── TiviShowDao.kt │ │ ├── TrendingDao.kt │ │ ├── UserDao.kt │ │ └── WatchedShowDao.kt │ │ └── db │ │ └── DatabaseTransactionRunner.kt ├── episodes │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── episodes │ │ ├── EpisodeBinds.kt │ │ ├── EpisodeLastRequestStore.kt │ │ ├── EpisodeWatchLastRequestStore.kt │ │ ├── EpisodeWatchStore.kt │ │ ├── SeasonLastRequestStore.kt │ │ ├── SeasonsEpisodesRepository.kt │ │ ├── ShowSeasonsLastRequestStore.kt │ │ └── datasource │ │ ├── EpisodeDataSource.kt │ │ ├── EpisodeWatchesDataSource.kt │ │ └── SeasonsEpisodesDataSource.kt ├── followedshows │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── followedshows │ │ ├── FollowedShowsBinds.kt │ │ ├── FollowedShowsDataSource.kt │ │ ├── FollowedShowsLastRequestStore.kt │ │ ├── FollowedShowsRepository.kt │ │ └── TraktFollowedShowsDataSource.kt ├── legacy │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ ├── lastrequests │ │ ├── EntityLastRequestStore.kt │ │ └── GroupLastRequestStore.kt │ │ ├── mappers │ │ ├── EpisodeIdToTraktIdMapper.kt │ │ ├── Mapper.kt │ │ ├── Mappers.kt │ │ ├── SeasonIdToTraktIdMapper.kt │ │ ├── ShowIdToTmdbIdMapper.kt │ │ ├── ShowIdToTraktIdMapper.kt │ │ ├── ShowIdToTraktOrImdbIdMapper.kt │ │ ├── TmdbEpisodeDetailToEpisode.kt │ │ ├── TmdbEpisodeToEpisode.kt │ │ ├── TmdbSeasonDetailToSeason.kt │ │ ├── TmdbSeasonToSeason.kt │ │ ├── TmdbSeasonToSeasonWithEpisodes.kt │ │ ├── TmdbShowDetailToShowImages.kt │ │ ├── TmdbShowDetailToTiviShow.kt │ │ ├── TmdbShowPageResultToTiviShows.kt │ │ ├── TmdbShowToTiviShow.kt │ │ ├── TraktBaseShowToWatchedShowEntry.kt │ │ ├── TraktEpisodeToEpisode.kt │ │ ├── TraktHistoryEntryToEpisode.kt │ │ ├── TraktHistoryItemToEpisodeWatchEntry.kt │ │ ├── TraktListItemToFollowedShowEntry.kt │ │ ├── TraktListItemToTiviShow.kt │ │ ├── TraktSeasonToSeason.kt │ │ ├── TraktSeasonToSeasonWithEpisodes.kt │ │ ├── TraktShowToTiviShow.kt │ │ ├── TraktStatusToShowStatus.kt │ │ ├── TraktTrendingShowToTiviShow.kt │ │ ├── TraktTrendingShowToTrendingShowEntry.kt │ │ └── UserToTraktUser.kt │ │ └── util │ │ ├── ItemSyncer.kt │ │ └── StoreExtensions.kt ├── licenses │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── data │ │ │ └── licenses │ │ │ ├── LicenseDataPlatformComponent.kt │ │ │ └── fetcher │ │ │ └── AndroidLicensesFetcherImpl.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── data │ │ │ └── licenses │ │ │ ├── LicenseDataComponent.kt │ │ │ ├── LicensesState.kt │ │ │ ├── fetcher │ │ │ └── LicensesFetcher.kt │ │ │ └── store │ │ │ ├── LicensesStore.kt │ │ │ └── LicensesStoreImpl.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── data │ │ │ └── licenses │ │ │ ├── LicenseDataPlatformComponent.kt │ │ │ └── fetcher │ │ │ └── IosLicensesFetcherImpl.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── licenses │ │ ├── LicenseDataPlatformComponent.kt │ │ └── fetcher │ │ └── JvmLicensesFetcherImpl.kt ├── models │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ ├── compoundmodels │ │ ├── EntryWithShow.kt │ │ ├── EpisodeWithSeason.kt │ │ ├── EpisodeWithWatches.kt │ │ ├── LibraryShow.kt │ │ ├── SeasonWithEpisodes.kt │ │ ├── SeasonWithEpisodesAndWatches.kt │ │ ├── SeasonWithShow.kt │ │ ├── ShowSeasonEpisode.kt │ │ └── UpNextEntry.kt │ │ ├── imagemodels │ │ ├── EpisodeImageModel.kt │ │ ├── ImageModel.kt │ │ ├── SeasonImageModel.kt │ │ └── ShowImageModel.kt │ │ ├── models │ │ ├── ActionDate.kt │ │ ├── AnticipatedShowEntry.kt │ │ ├── Entry.kt │ │ ├── Episode.kt │ │ ├── EpisodeWatchEntry.kt │ │ ├── FollowedShowEntry.kt │ │ ├── Genre.kt │ │ ├── LastRequest.kt │ │ ├── Notification.kt │ │ ├── PendingAction.kt │ │ ├── PopularShowEntry.kt │ │ ├── RecommendedShowEntry.kt │ │ ├── RelatedShowEntry.kt │ │ ├── Request.kt │ │ ├── Season.kt │ │ ├── ShowImages.kt │ │ ├── ShowStatus.kt │ │ ├── ShowTmdbImage.kt │ │ ├── SortOption.kt │ │ ├── TiviEntity.kt │ │ ├── TiviShow.kt │ │ ├── TraktUser.kt │ │ ├── TrendingShowEntry.kt │ │ └── WatchedShowEntry.kt │ │ ├── util │ │ ├── DateTimeUtils.kt │ │ ├── ImageEntityUtils.kt │ │ └── MergeShowUtils.kt │ │ └── views │ │ ├── ShowsNextToWatch.kt │ │ └── ShowsWatchStats.kt ├── popularshows │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── popularshows │ │ ├── PopularShowsBinds.kt │ │ ├── PopularShowsDataSource.kt │ │ ├── PopularShowsLastRequestStore.kt │ │ ├── PopularShowsStore.kt │ │ └── TraktPopularShowsDataSource.kt ├── recommendedshows │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── recommendedshows │ │ ├── RecommendedShowsBinds.kt │ │ ├── RecommendedShowsDataSource.kt │ │ ├── RecommendedShowsLastRequestStore.kt │ │ ├── RecommendedShowsStore.kt │ │ └── TraktRecommendedShowsDataSource.kt ├── relatedshows │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── relatedshows │ │ ├── RelatedShowsBinds.kt │ │ ├── RelatedShowsDataSource.kt │ │ ├── RelatedShowsLastRequestStore.kt │ │ ├── RelatedShowsStore.kt │ │ ├── TmdbRelatedShowsDataSourceImpl.kt │ │ └── TraktRelatedShowsDataSourceImpl.kt ├── search │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── search │ │ ├── SearchBinds.kt │ │ ├── SearchDataSource.kt │ │ ├── SearchRepository.kt │ │ └── TmdbSearchDataSource.kt ├── showimages │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── showimages │ │ ├── ShowImagesBinds.kt │ │ ├── ShowImagesDataSource.kt │ │ ├── ShowImagesLastRequestStore.kt │ │ ├── ShowImagesStore.kt │ │ └── TmdbShowImagesDataSource.kt ├── shows │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── shows │ │ ├── ShowDataSource.kt │ │ ├── ShowLastRequestStore.kt │ │ ├── ShowStore.kt │ │ ├── ShowsBinds.kt │ │ ├── TmdbShowDataSourceImpl.kt │ │ └── TraktShowDataSourceImpl.kt ├── test │ ├── build.gradle.kts │ └── src │ │ ├── androidUnitTest │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── data │ │ │ └── DatabaseTest.kt │ │ ├── commonTest │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ ├── data │ │ │ ├── DatabaseTest.kt │ │ │ ├── dao │ │ │ │ ├── EpisodeWatchEntryTest.kt │ │ │ │ ├── EpisodesTest.kt │ │ │ │ └── SeasonsTest.kt │ │ │ └── repositories │ │ │ │ ├── FollowedShowRepositoryTest.kt │ │ │ │ └── SeasonsEpisodesRepositoryTest.kt │ │ │ └── utils │ │ │ ├── AuthorizedAuthStore.kt │ │ │ ├── Dispatchers.kt │ │ │ ├── FakeEpisodeDataSource.kt │ │ │ ├── FakeEpisodeWatchesDataSource.kt │ │ │ ├── FakeFollowedShowsDataSource.kt │ │ │ ├── FakeSeasonsEpisodesDataSource.kt │ │ │ ├── Fakes.kt │ │ │ ├── ObjectGraph.kt │ │ │ ├── SampleData.kt │ │ │ └── TestTransactionRunner.kt │ │ ├── jvmTest │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── data │ │ │ └── DatabaseTest.kt │ │ └── nativeTest │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── DatabaseTest.kt ├── traktauth │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── data │ │ │ └── traktauth │ │ │ ├── AndroidTraktLoginAction.kt │ │ │ ├── AndroidTraktRefreshTokenAction.kt │ │ │ ├── AppAuthAuthStateWrapper.kt │ │ │ ├── LoginTraktActivityResultContract.kt │ │ │ ├── TraktAuthComponent.kt │ │ │ ├── TraktAuthInitializer.kt │ │ │ └── store │ │ │ ├── BlockStoreAuthStore.kt │ │ │ ├── PreferencesAuthStore.kt │ │ │ └── TiviAuthStore.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── data │ │ │ └── traktauth │ │ │ ├── AuthState.kt │ │ │ ├── TraktAuthComponent.kt │ │ │ ├── TraktAuthRepository.kt │ │ │ ├── TraktAuthState.kt │ │ │ ├── TraktLoginAction.kt │ │ │ ├── TraktOAuthInfo.kt │ │ │ ├── TraktRefreshTokenAction.kt │ │ │ └── store │ │ │ └── AuthStore.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── data │ │ │ └── traktauth │ │ │ ├── IosAuthStore.kt │ │ │ └── TraktAuthComponent.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── traktauth │ │ ├── DesktopAuthStore.kt │ │ ├── DesktopTraktLoginAction.kt │ │ ├── DesktopTraktRefreshTokenAction.kt │ │ └── TraktAuthComponent.kt ├── traktusers │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── traktusers │ │ ├── TraktUsersBinds.kt │ │ ├── TraktUsersDataSource.kt │ │ ├── TraktUsersLastRequestStore.kt │ │ ├── TraktUsersRepository.kt │ │ └── UsersDataSource.kt ├── trendingshows │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── data │ │ └── trendingshows │ │ ├── TraktTrendingShowsDataSource.kt │ │ ├── TrendingShowsBinds.kt │ │ ├── TrendingShowsDataSource.kt │ │ ├── TrendingShowsLastRequestStore.kt │ │ └── TrendingShowsStore.kt └── watchedshows │ ├── build.gradle.kts │ └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── data │ └── watchedshows │ ├── TraktWatchedShowsDataSource.kt │ ├── WatchedShowsBinds.kt │ ├── WatchedShowsDataSource.kt │ ├── WatchedShowsLastRequestStore.kt │ └── WatchedShowsStore.kt ├── desktop-app ├── build.gradle.kts └── src │ └── jvmMain │ └── kotlin │ └── app │ └── tivi │ └── Main.kt ├── docs ├── _config.yml └── privacypolicy.md ├── domain ├── build.gradle.kts └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── domain │ ├── Interactor.kt │ ├── PaginatedEntryRemoteMediator.kt │ ├── RefreshOnlyRemoteMediator.kt │ ├── interactors │ ├── AddEpisodeWatch.kt │ ├── ChangeSeasonFollowStatus.kt │ ├── ChangeSeasonWatchedStatus.kt │ ├── ChangeShowFollowStatus.kt │ ├── ClearUserDetails.kt │ ├── FetchLicensesList.kt │ ├── GetTraktAuthState.kt │ ├── LoginTrakt.kt │ ├── LogoutTrakt.kt │ ├── RefreshTraktTokens.kt │ ├── RemoveEpisodeWatch.kt │ ├── RemoveEpisodeWatches.kt │ ├── ScheduleDebugEpisodeNotification.kt │ ├── ScheduleEpisodeNotifications.kt │ ├── SearchShows.kt │ ├── UpdateAnticipatedShows.kt │ ├── UpdateEpisodeDetails.kt │ ├── UpdateLibraryShows.kt │ ├── UpdatePopularShows.kt │ ├── UpdateRecommendedShows.kt │ ├── UpdateRelatedShows.kt │ ├── UpdateShowDetails.kt │ ├── UpdateShowImages.kt │ ├── UpdateShowSeasons.kt │ ├── UpdateTmdbConfig.kt │ ├── UpdateTrendingShows.kt │ ├── UpdateUpNextEpisodes.kt │ └── UpdateUserDetails.kt │ └── observers │ ├── ObserveAnticipatedShows.kt │ ├── ObserveEpisodeDetails.kt │ ├── ObserveEpisodeWatches.kt │ ├── ObserveNextShowEpisodesToWatch.kt │ ├── ObservePagedAnticipatedShows.kt │ ├── ObservePagedLibraryShows.kt │ ├── ObservePagedPopularShows.kt │ ├── ObservePagedRecommendedShows.kt │ ├── ObservePagedTrendingShows.kt │ ├── ObservePagedUpNextShows.kt │ ├── ObservePopularShows.kt │ ├── ObserveRecommendedShows.kt │ ├── ObserveRelatedShows.kt │ ├── ObserveShowDetails.kt │ ├── ObserveShowDetailsForEpisodeId.kt │ ├── ObserveShowFollowStatus.kt │ ├── ObserveShowImages.kt │ ├── ObserveShowNextEpisodeToWatch.kt │ ├── ObserveShowSeasonsEpisodesWatches.kt │ ├── ObserveShowViewStats.kt │ ├── ObserveTraktAuthState.kt │ ├── ObserveTrendingShows.kt │ └── ObserveUserDetails.kt ├── fastlane ├── Fastfile ├── Matchfile ├── metadata │ └── android │ │ └── en_US │ │ ├── full_description.txt │ │ ├── images │ │ ├── featureGraphic.png │ │ ├── icon.png │ │ └── phoneScreenshots │ │ │ ├── 0_home.png │ │ │ ├── 1_show_details.png │ │ │ ├── 2_upnext.png │ │ │ ├── 3_library.png │ │ │ └── 4_search.png │ │ ├── short_description.txt │ │ ├── title.txt │ │ └── video.txt └── screenshots │ ├── en-US │ ├── iPad Pro (12.9-inch) (3rd generation)-1_Home.png │ ├── iPad Pro (12.9-inch) (3rd generation)-2_ShowDetails.png │ ├── iPad Pro (12.9-inch) (3rd generation)-3_UpNext.png │ ├── iPad Pro (12.9-inch) (3rd generation)-4_Library.png │ ├── iPad Pro (12.9-inch) (3rd generation)-5_Search.png │ ├── iPhone 15 Pro Max-1_Home.png │ ├── iPhone 15 Pro Max-2_ShowDetails.png │ ├── iPhone 15 Pro Max-3_UpNext.png │ ├── iPhone 15 Pro Max-4_Library.png │ ├── iPhone 15 Pro Max-5_Search.png │ ├── iPhone SE (3rd generation)-1_Home.png │ ├── iPhone SE (3rd generation)-2_ShowDetails.png │ ├── iPhone SE (3rd generation)-3_UpNext.png │ ├── iPhone SE (3rd generation)-4_Library.png │ └── iPhone SE (3rd generation)-5_Search.png │ └── screenshots.html ├── gradle.properties ├── gradle ├── build-logic │ ├── convention │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── gradle │ │ │ ├── Android.kt │ │ │ ├── AndroidApplicationConventionPlugin.kt │ │ │ ├── AndroidApplicationLicensesHandler.kt │ │ │ ├── AndroidLibraryConventionPlugin.kt │ │ │ ├── AndroidTestConventionPlugin.kt │ │ │ ├── AssetCopyTask.kt │ │ │ ├── ComposeMultiplatformConventionPlugin.kt │ │ │ ├── IosLicensesHandler.kt │ │ │ ├── Java.kt │ │ │ ├── Kotlin.kt │ │ │ ├── KotlinAndroidConventionPlugin.kt │ │ │ ├── KotlinMultiplatformConventionPlugin.kt │ │ │ ├── Licensee.kt │ │ │ ├── RootConventionPlugin.kt │ │ │ ├── Spotless.kt │ │ │ ├── VersionCatalog.kt │ │ │ └── Versions.kt │ ├── gradle.properties │ └── settings.gradle.kts ├── dependencyGraph.gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── ios-app └── Tivi │ ├── Podfile │ ├── Podfile.lock │ ├── Settings.bundle │ └── Root.plist │ ├── Tivi.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── swiftpm │ │ │ └── Package.resolved │ └── xcshareddata │ │ └── xcschemes │ │ ├── Tivi Prod (StoreKit).xcscheme │ │ ├── Tivi Prod.xcscheme │ │ ├── Tivi QA.xcscheme │ │ └── UITests.xcscheme │ ├── Tivi.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── Tivi │ ├── Assets.xcassets │ │ ├── AppIcon-QA.appiconset │ │ │ ├── AppIcon-QA.png │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── AppIcon.png │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Auth.swift │ ├── ContentView.swift │ ├── GoogleService-Info.plist │ ├── Info.plist │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ └── TiviApp.swift │ ├── UITests │ ├── Extensions.swift │ ├── Screenshots.swift │ └── SnapshotHelper.swift │ └── xcconfig │ ├── Project.xcconfig │ ├── Tivi-Prod-Debug.xcconfig │ ├── Tivi-Prod-Release.xcconfig │ ├── Tivi-QA-Debug.xcconfig │ ├── Tivi-QA-Release.xcconfig │ ├── UITests.xcconfig │ └── common │ ├── Debug.xcconfig │ ├── Prod.xcconfig │ ├── QA.xcconfig │ └── Release.xcconfig ├── release ├── GoogleService-Info.plist.gpg ├── app-debug.jks ├── app-release.gpg ├── clean-secrets.sh ├── decrypt-secrets.sh ├── encrypt-secrets.sh ├── google-services.gpg └── play-account.gpg ├── renovate.json ├── room2sqld.py ├── settings.gradle.kts ├── shared ├── common │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── inject │ │ │ ├── SharedActivityComponent.kt │ │ │ └── SharedPlatformApplicationComponent.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ ├── appinitializers │ │ │ └── AppInitializers.kt │ │ │ └── inject │ │ │ ├── SharedApplicationComponent.kt │ │ │ └── SharedUiComponent.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── inject │ │ │ └── SharedPlatformApplicationComponent.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── inject │ │ └── SharedPlatformApplicationComponent.kt ├── prod │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── inject │ │ │ ├── AndroidActivityComponent.kt │ │ │ └── AndroidApplicationComponent.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── inject │ │ │ ├── ProdApplicationComponent.kt │ │ │ └── ProdUiComponent.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── app │ │ │ └── tivi │ │ │ └── inject │ │ │ ├── HomeUiControllerComponent.kt │ │ │ └── IosApplicationComponent.kt │ │ └── jvmMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── inject │ │ ├── DesktopApplicationComponent.kt │ │ └── WindowComponent.kt └── qa │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ ├── androidMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── inject │ │ ├── AndroidActivityComponent.kt │ │ └── AndroidApplicationComponent.kt │ ├── commonMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── inject │ │ ├── QaApplicationComponent.kt │ │ └── QaUiComponent.kt │ ├── iosMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── inject │ │ ├── HomeUiControllerComponent.kt │ │ └── IosApplicationComponent.kt │ └── jvmMain │ └── kotlin │ └── app │ └── tivi │ └── inject │ ├── DesktopApplicationComponent.kt │ └── WindowComponent.kt ├── spotless ├── cb-copyright.txt └── google-copyright.txt ├── tasks ├── build.gradle.kts └── src │ ├── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── tasks │ │ ├── AndroidTasks.kt │ │ ├── BootBroadcastReceiver.kt │ │ ├── ScheduleEpisodeNotificationsWorker.kt │ │ ├── SyncLibraryShowsWorker.kt │ │ ├── TasksPlatformComponent.kt │ │ └── TiviWorkerFactory.kt │ ├── commonMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── tasks │ │ ├── Tasks.kt │ │ ├── TasksComponent.kt │ │ └── TasksInitializer.kt │ ├── iosMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── tasks │ │ ├── IosTasks.kt │ │ └── TasksPlatformComponent.kt │ └── jvmMain │ └── kotlin │ └── app │ └── tivi │ └── tasks │ └── TasksPlatformComponent.kt ├── thirdparty └── androidx │ └── paging │ └── compose │ ├── build.gradle.kts │ └── src │ ├── androidMain │ └── kotlin │ │ └── PagingPlaceholders.android.kt │ ├── commonMain │ └── kotlin │ │ ├── LazyFoundationExtensions.kt │ │ ├── LazyPagingItems.kt │ │ └── PagingPlaceholders.kt │ ├── iosMain │ └── kotlin │ │ └── PagingPlaceholders.ios.kt │ └── jvmMain │ └── kotlin │ └── PagingPlaceholders.jvm.kt └── ui ├── account ├── build.gradle.kts ├── lint-baseline.xml └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── account │ ├── AccountComponent.kt │ ├── AccountPresenter.kt │ ├── AccountUi.kt │ └── AccountUiState.kt ├── anticipated ├── build.gradle.kts ├── lint-baseline.xml └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── home │ └── anticipated │ ├── AnticipatedShows.kt │ ├── AnticipatedShowsComponent.kt │ ├── AnticipatedShowsPresenter.kt │ └── AnticipatedShowsUiState.kt ├── developer ├── log │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── developer │ │ └── log │ │ ├── DevLog.kt │ │ ├── DevLogComponent.kt │ │ ├── DevLogPresenter.kt │ │ └── DevLogUiState.kt ├── notifications │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── developer │ │ └── notifications │ │ ├── DevNotifications.kt │ │ ├── DevNotificationsComponent.kt │ │ ├── DevNotificationsPresenter.kt │ │ └── DevNotificationsUiState.kt └── settings │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── settings │ └── developer │ ├── DevSettings.kt │ ├── DevSettingsComponent.kt │ ├── DevSettingsPresenter.kt │ └── DevSettingsUiState.kt ├── discover ├── build.gradle.kts ├── lint-baseline.xml └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── home │ └── discover │ ├── Discover.kt │ ├── DiscoverComponent.kt │ ├── DiscoverPresenter.kt │ └── DiscoverUiState.kt ├── episode ├── details │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── episodedetails │ │ ├── EpisodeDetails.kt │ │ ├── EpisodeDetailsComponent.kt │ │ ├── EpisodeDetailsPresenter.kt │ │ └── EpisodeDetailsUiState.kt └── track │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── episode │ └── track │ ├── EpisodeTrack.kt │ ├── EpisodeTrackComponent.kt │ ├── EpisodeTrackPresenter.kt │ └── EpisodeTrackUiState.kt ├── library ├── build.gradle.kts ├── lint-baseline.xml └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── home │ └── library │ ├── Library.kt │ ├── LibraryComponent.kt │ ├── LibraryPresenter.kt │ └── LibraryUiState.kt ├── licenses ├── build.gradle.kts ├── lint-baseline.xml └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── settings │ └── licenses │ ├── Licenses.kt │ ├── LicensesComponent.kt │ ├── LicensesPresenter.kt │ └── LicensesUiState.kt ├── popular ├── build.gradle.kts ├── lint-baseline.xml └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── home │ └── popular │ ├── PopularShows.kt │ ├── PopularShowsComponent.kt │ ├── PopularShowsPresenter.kt │ └── PopularShowsUiState.kt ├── recommended ├── build.gradle.kts ├── lint-baseline.xml └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── home │ └── recommended │ ├── Recommended.kt │ ├── RecommendedShowsComponent.kt │ ├── RecommendedShowsPresenter.kt │ └── RecommendedShowsUiState.kt ├── root ├── build.gradle.kts ├── lint-baseline.xml └── src │ ├── commonMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── home │ │ ├── Home.kt │ │ ├── RootUiComponent.kt │ │ ├── RootViewModel.kt │ │ └── TiviContent.kt │ └── iosMain │ └── kotlin │ └── app │ └── tivi │ └── home │ └── TiviUiViewController.kt ├── search ├── build.gradle.kts ├── lint-baseline.xml └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── home │ └── search │ ├── Search.kt │ ├── SearchComponent.kt │ ├── SearchPresenter.kt │ └── SearchUiState.kt ├── settings ├── build.gradle.kts ├── lint-baseline.xml └── src │ ├── androidMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── settings │ │ └── Platform.kt │ ├── commonMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── settings │ │ ├── Platform.kt │ │ ├── Settings.kt │ │ ├── SettingsComponent.kt │ │ ├── SettingsPresenter.kt │ │ └── SettingsUiState.kt │ ├── iosMain │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── settings │ │ └── Platform.kt │ └── jvmMain │ └── kotlin │ └── app │ └── tivi │ └── settings │ └── Platform.kt ├── show ├── details │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── tivi │ │ └── showdetails │ │ └── details │ │ ├── ShowDetails.kt │ │ ├── ShowDetailsComponent.kt │ │ ├── ShowDetailsPresenter.kt │ │ └── ShowDetailsUiState.kt └── seasons │ ├── build.gradle.kts │ ├── lint-baseline.xml │ └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── showdetails │ └── seasons │ ├── ShowSeasons.kt │ ├── ShowSeasonsComponent.kt │ ├── ShowSeasonsPresenter.kt │ └── ShowSeasonsUiState.kt ├── trending ├── build.gradle.kts ├── lint-baseline.xml └── src │ └── commonMain │ └── kotlin │ └── app │ └── tivi │ └── home │ └── trending │ ├── Trending.kt │ ├── TrendingShowsComponent.kt │ ├── TrendingShowsPresenter.kt │ └── TrendingShowsUiState.kt └── upnext ├── build.gradle.kts ├── lint-baseline.xml └── src └── commonMain └── kotlin └── app └── tivi └── home └── upnext ├── UpNext.kt ├── UpNextComponent.kt ├── UpNextPresenter.kt └── UpNextUiState.kt /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 2 6 | indent_style = space 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.{kt,kts}] 11 | ij_kotlin_imports_layout = * 12 | ktlint_code_style = intellij_idea 13 | ktlint_standard_discouraged-comment-location = disabled 14 | ktlint_standard_function-expression-body = disabled 15 | ktlint_function_naming_ignore_when_annotated_with = Composable 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.bat text eol=crlf 4 | *.jar binary 5 | -------------------------------------------------------------------------------- /.github/workflows/gradle-wrapper.yaml: -------------------------------------------------------------------------------- 1 | name: gradle-wrapper 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'gradlew' 7 | - 'gradlew.bat' 8 | - 'gradle/wrapper/' 9 | 10 | jobs: 11 | validate: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: gradle/wrapper-validation-action@v3 16 | -------------------------------------------------------------------------------- /.github/workflows/todo.yml: -------------------------------------------------------------------------------- 1 | name: TODO <> Issues 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 14 | - uses: alstr/todo-to-issue-action@v5 15 | -------------------------------------------------------------------------------- /.idea/checkstyle-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/copyright/Chris_Banes_Apache_v2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/copyright/Google_Apache_v2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/dictionaries/chris.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | spdx 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/dictionaries/chrisbanes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tmdb 5 | trakt 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/.idea/icon.png -------------------------------------------------------------------------------- /.idea/inspectionProfiles/ktlint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.3.4 2 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | included: 2 | - ios-app/Tivi 3 | 4 | excluded: 5 | - ios-app/Tivi/UITests/SnapshotHelper.swift 6 | - ios-app/Tivi/Pods 7 | 8 | strict: true 9 | 10 | disabled_rules: 11 | - todo 12 | -------------------------------------------------------------------------------- /.xcode-version: -------------------------------------------------------------------------------- 1 | 15.3 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'cocoapods', '~> 1.15' 4 | gem 'fastlane' 5 | gem 'screengrab' 6 | -------------------------------------------------------------------------------- /android-app/app/benchmark-rules.pro: -------------------------------------------------------------------------------- 1 | -dontobfuscate 2 | -dontwarn com.google.errorprone.annotations.InlineMe 3 | -------------------------------------------------------------------------------- /android-app/app/proguard-rules-chucker.pro: -------------------------------------------------------------------------------- 1 | # Chucker uses GSON and TypeToken: 2 | # https://r8.googlesource.com/r8/+/refs/heads/main/compatibility-faq.md#gson-with-full-mode 3 | -keepattributes Signature 4 | -keep class com.google.gson.reflect.TypeToken { *; } 5 | -keep class * extends com.google.gson.reflect.TypeToken -------------------------------------------------------------------------------- /android-app/app/src/main/res/drawable-anydpi-v26/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/android-app/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/android-app/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android-app/app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /android-app/app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 11 | 12 | 13 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /android-app/benchmark/src/main/kotlin/app/tivi/benchmark/Utils.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.benchmark 5 | 6 | import android.Manifest 7 | import android.os.Build 8 | import androidx.test.uiautomator.UiDevice 9 | 10 | fun UiDevice.allowNotifications(packageName: String) { 11 | if (Build.VERSION.SDK_INT >= 33) { 12 | executeShellCommand("pm grant $packageName ${Manifest.permission.POST_NOTIFICATIONS}") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android-app/common-test/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.android.library") 7 | id("app.tivi.kotlin.android") 8 | } 9 | 10 | android { 11 | namespace = "app.tivi.app.test" 12 | } 13 | 14 | dependencies { 15 | implementation(projects.core.base) 16 | api(libs.androidx.uiautomator) 17 | } 18 | -------------------------------------------------------------------------------- /api/tmdb/src/commonMain/kotlin/app/tivi/tmdb/TmdbInitializer.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.tmdb 5 | 6 | import app.tivi.appinitializers.AppInitializer 7 | import app.tivi.inject.ApplicationCoroutineScope 8 | import app.tivi.util.launchOrThrow 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class TmdbInitializer( 13 | private val tmdbManager: Lazy, 14 | private val scope: ApplicationCoroutineScope, 15 | ) : AppInitializer { 16 | override fun initialize() { 17 | scope.launchOrThrow { 18 | tmdbManager.value.refreshConfiguration() 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /api/tmdb/src/commonMain/kotlin/app/tivi/tmdb/TmdbOAuthInfo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.tmdb 5 | 6 | data class TmdbOAuthInfo( 7 | val apiKey: String, 8 | ) 9 | -------------------------------------------------------------------------------- /art/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/art/account.png -------------------------------------------------------------------------------- /art/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/art/banner.png -------------------------------------------------------------------------------- /art/episode-details.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/art/episode-details.gif -------------------------------------------------------------------------------- /art/show-details.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/art/show-details.gif -------------------------------------------------------------------------------- /art/theme-baseline.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/art/theme-baseline.sketch -------------------------------------------------------------------------------- /art/tivi-art.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/art/tivi-art.sketch -------------------------------------------------------------------------------- /common/imageloading/src/androidMain/kotlin/app/tivi/common/imageloading/ImageLoadingPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.imageloading 5 | 6 | import android.app.Application 7 | import coil3.PlatformContext 8 | import me.tatarka.inject.annotations.Provides 9 | import okio.FileSystem 10 | 11 | actual interface ImageLoadingPlatformComponent { 12 | @Provides 13 | fun providePlatformContext(application: Application): PlatformContext = application 14 | 15 | @Provides 16 | fun provideFileSystem(): FileSystem = FileSystem.SYSTEM 17 | } 18 | -------------------------------------------------------------------------------- /common/imageloading/src/iosMain/kotlin/app/tivi/common/imageloading/ImageLoadingPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.imageloading 5 | 6 | import coil3.PlatformContext 7 | import me.tatarka.inject.annotations.Provides 8 | import okio.FileSystem 9 | 10 | actual interface ImageLoadingPlatformComponent { 11 | @Provides 12 | fun providePlatformContext(): PlatformContext = PlatformContext.INSTANCE 13 | 14 | @Provides 15 | fun provideFileSystem(): FileSystem = FileSystem.SYSTEM 16 | } 17 | -------------------------------------------------------------------------------- /common/imageloading/src/jvmMain/kotlin/app/tivi/common/imageloading/ImageLoadingPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.imageloading 5 | 6 | import coil3.PlatformContext 7 | import me.tatarka.inject.annotations.Provides 8 | import okio.FileSystem 9 | 10 | actual interface ImageLoadingPlatformComponent { 11 | @Provides 12 | fun providePlatformContext(): PlatformContext = PlatformContext.INSTANCE 13 | 14 | @Provides 15 | fun provideFileSystem(): FileSystem = FileSystem.SYSTEM 16 | } 17 | -------------------------------------------------------------------------------- /common/ui/circuit/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /common/ui/circuit/src/commonMain/kotlin/app/tivi/navigation/LocalNavigator.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.navigation 5 | 6 | import androidx.compose.runtime.staticCompositionLocalOf 7 | import com.slack.circuit.runtime.Navigator 8 | 9 | /** 10 | * Yes, I don't like this either. I'm a bit stuck though due to 11 | * https://github.com/slackhq/circuit/issues/653 12 | */ 13 | val LocalNavigator = staticCompositionLocalOf { Navigator.NoOp } 14 | -------------------------------------------------------------------------------- /common/ui/compose/src/androidMain/kotlin/app/tivi/common/compose/Coil.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import androidx.compose.ui.graphics.ImageBitmap 7 | import androidx.compose.ui.graphics.asImageBitmap 8 | import coil3.Image 9 | import coil3.annotation.ExperimentalCoilApi 10 | import coil3.toBitmap 11 | 12 | @OptIn(ExperimentalCoilApi::class) 13 | internal actual fun Image.toComposeImageBitmap(): ImageBitmap = toBitmap().asImageBitmap() 14 | -------------------------------------------------------------------------------- /common/ui/compose/src/androidMain/kotlin/app/tivi/common/compose/ColorExtractor.android.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import coil3.request.ImageRequest 7 | import coil3.request.allowHardware 8 | import coil3.request.allowRgb565 9 | 10 | internal actual fun ImageRequest.Builder.prepareForColorExtractor(): ImageRequest.Builder { 11 | return allowHardware(false) 12 | .allowRgb565(true) 13 | } 14 | -------------------------------------------------------------------------------- /common/ui/compose/src/androidMain/kotlin/app/tivi/common/compose/ReportDrawnWhen.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import android.os.Build 7 | import androidx.compose.runtime.Composable 8 | 9 | @Composable 10 | actual fun ReportDrawnWhen(predicate: () -> Boolean) { 11 | // ReportDrawnWhen routinely causes crashes on API < 25: 12 | // https://issuetracker.google.com/issues/260506820 13 | if (Build.VERSION.SDK_INT >= 25) { 14 | androidx.activity.compose.ReportDrawnWhen(predicate) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /common/ui/compose/src/androidMain/kotlin/app/tivi/common/compose/ui/Icon.android.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose.ui 5 | 6 | import androidx.compose.material.icons.Icons 7 | import androidx.compose.material.icons.automirrored.filled.ArrowBack 8 | import androidx.compose.ui.graphics.vector.ImageVector 9 | 10 | actual val Icons.AutoMirrored.Filled.ArrowBackForPlatform: ImageVector 11 | get() = ArrowBack 12 | -------------------------------------------------------------------------------- /common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/Modifier.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import androidx.compose.ui.Modifier 7 | 8 | inline fun Modifier.thenIf( 9 | condition: Boolean, 10 | whenFalse: Modifier.() -> Modifier = { this }, 11 | whenTrue: Modifier.() -> Modifier, 12 | ): Modifier = if (condition) whenTrue() else whenFalse() 13 | -------------------------------------------------------------------------------- /common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/ReportDrawnWhen.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import androidx.compose.runtime.Composable 7 | 8 | @Composable 9 | expect fun ReportDrawnWhen(predicate: () -> Boolean) 10 | -------------------------------------------------------------------------------- /common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/TiviPreferenceExtensions.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2020, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.collectAsState 8 | import app.tivi.settings.Preference 9 | 10 | @Composable 11 | fun Preference.collectAsState() = flow.collectAsState(defaultValue) 12 | -------------------------------------------------------------------------------- /common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/WindowSizeClass.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import androidx.compose.material3.windowsizeclass.WindowSizeClass 7 | import androidx.compose.runtime.staticCompositionLocalOf 8 | 9 | val LocalWindowSizeClass = staticCompositionLocalOf { 10 | error("No WindowSizeClass available") 11 | } 12 | -------------------------------------------------------------------------------- /common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/theme/Platform.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2020, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose.theme 5 | 6 | import androidx.compose.material3.ColorScheme 7 | import androidx.compose.runtime.Composable 8 | 9 | @Composable 10 | internal expect fun colorScheme( 11 | useDarkColors: Boolean, 12 | useDynamicColors: Boolean, 13 | ): ColorScheme 14 | -------------------------------------------------------------------------------- /common/ui/compose/src/commonMain/kotlin/app/tivi/common/compose/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2020, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose.theme 5 | 6 | import androidx.compose.foundation.shape.CircleShape 7 | import androidx.compose.material3.Shapes 8 | 9 | val TiviShapes = Shapes(small = CircleShape) 10 | -------------------------------------------------------------------------------- /common/ui/compose/src/iosMain/kotlin/app/tivi/common/compose/Coil.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import androidx.compose.ui.graphics.ImageBitmap 7 | import androidx.compose.ui.graphics.asComposeImageBitmap 8 | import coil3.Image 9 | import coil3.annotation.ExperimentalCoilApi 10 | import coil3.toBitmap 11 | 12 | @OptIn(ExperimentalCoilApi::class) 13 | internal actual fun Image.toComposeImageBitmap(): ImageBitmap = toBitmap().asComposeImageBitmap() 14 | -------------------------------------------------------------------------------- /common/ui/compose/src/iosMain/kotlin/app/tivi/common/compose/ColorExtractor.ios.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import coil3.request.ImageRequest 7 | 8 | internal actual fun ImageRequest.Builder.prepareForColorExtractor(): ImageRequest.Builder = this 9 | -------------------------------------------------------------------------------- /common/ui/compose/src/iosMain/kotlin/app/tivi/common/compose/ReportDrawnWhen.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import androidx.compose.runtime.Composable 7 | 8 | @Composable 9 | actual fun ReportDrawnWhen(predicate: () -> Boolean) { 10 | } 11 | -------------------------------------------------------------------------------- /common/ui/compose/src/iosMain/kotlin/app/tivi/common/compose/theme/Platform.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose.theme 5 | 6 | import androidx.compose.material3.ColorScheme 7 | import androidx.compose.runtime.Composable 8 | 9 | @Composable 10 | internal actual fun colorScheme( 11 | useDarkColors: Boolean, 12 | useDynamicColors: Boolean, 13 | ): ColorScheme = when { 14 | useDarkColors -> TiviDarkColors 15 | else -> TiviLightColors 16 | } 17 | -------------------------------------------------------------------------------- /common/ui/compose/src/iosMain/kotlin/app/tivi/common/compose/ui/Icon.ios.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose.ui 5 | 6 | import androidx.compose.material.icons.Icons 7 | import androidx.compose.ui.graphics.vector.ImageVector 8 | 9 | actual val Icons.AutoMirrored.Filled.ArrowBackForPlatform: ImageVector 10 | get() = ArrowBackIosFixed 11 | -------------------------------------------------------------------------------- /common/ui/compose/src/jvmMain/kotlin/app/tivi/common/compose/Coil.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import androidx.compose.ui.graphics.ImageBitmap 7 | import androidx.compose.ui.graphics.asComposeImageBitmap 8 | import coil3.Image 9 | import coil3.annotation.ExperimentalCoilApi 10 | import coil3.toBitmap 11 | 12 | @OptIn(ExperimentalCoilApi::class) 13 | internal actual fun Image.toComposeImageBitmap(): ImageBitmap = toBitmap().asComposeImageBitmap() 14 | -------------------------------------------------------------------------------- /common/ui/compose/src/jvmMain/kotlin/app/tivi/common/compose/ColorExtractor.jvm.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import coil3.request.ImageRequest 7 | 8 | internal actual fun ImageRequest.Builder.prepareForColorExtractor(): ImageRequest.Builder = this 9 | -------------------------------------------------------------------------------- /common/ui/compose/src/jvmMain/kotlin/app/tivi/common/compose/ReportDrawnWhen.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose 5 | 6 | import androidx.compose.runtime.Composable 7 | 8 | @Composable 9 | actual fun ReportDrawnWhen(predicate: () -> Boolean) { 10 | } 11 | -------------------------------------------------------------------------------- /common/ui/compose/src/jvmMain/kotlin/app/tivi/common/compose/theme/Platform.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose.theme 5 | 6 | import androidx.compose.material3.ColorScheme 7 | import androidx.compose.runtime.Composable 8 | 9 | @Composable 10 | internal actual fun colorScheme( 11 | useDarkColors: Boolean, 12 | useDynamicColors: Boolean, 13 | ): ColorScheme = when { 14 | useDarkColors -> TiviDarkColors 15 | else -> TiviLightColors 16 | } 17 | -------------------------------------------------------------------------------- /common/ui/compose/src/jvmMain/kotlin/app/tivi/common/compose/ui/Icon.jvm.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.compose.ui 5 | 6 | import androidx.compose.material.icons.Icons 7 | import androidx.compose.ui.graphics.vector.ImageVector 8 | 9 | actual val Icons.AutoMirrored.Filled.ArrowBackForPlatform: ImageVector 10 | get() = TODO("Not yet implemented") 11 | -------------------------------------------------------------------------------- /common/ui/resources/src/androidMain/kotlin/app/tivi/util/String.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | actual fun String.fmt(vararg args: Any?): String = format(*args) 7 | -------------------------------------------------------------------------------- /common/ui/resources/src/commonMain/composeResources/font/dm_sans_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/common/ui/resources/src/commonMain/composeResources/font/dm_sans_bold.ttf -------------------------------------------------------------------------------- /common/ui/resources/src/commonMain/composeResources/font/dm_sans_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/common/ui/resources/src/commonMain/composeResources/font/dm_sans_medium.ttf -------------------------------------------------------------------------------- /common/ui/resources/src/commonMain/composeResources/font/dm_sans_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/common/ui/resources/src/commonMain/composeResources/font/dm_sans_regular.ttf -------------------------------------------------------------------------------- /common/ui/resources/src/commonMain/kotlin/app/tivi/common/ui/resources/Fonts.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.common.ui.resources 5 | 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.text.font.FontFamily 8 | import androidx.compose.ui.text.font.FontWeight 9 | import org.jetbrains.compose.resources.Font 10 | 11 | val DmSansFontFamily: FontFamily 12 | @Composable get() = FontFamily( 13 | Font(Res.font.dm_sans_regular, weight = FontWeight.Normal), 14 | Font(Res.font.dm_sans_medium, weight = FontWeight.Medium), 15 | Font(Res.font.dm_sans_bold, weight = FontWeight.Bold), 16 | ) 17 | -------------------------------------------------------------------------------- /common/ui/resources/src/commonMain/kotlin/app/tivi/util/String.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | expect fun String.fmt(vararg args: Any?): String 7 | -------------------------------------------------------------------------------- /common/ui/resources/src/jvmMain/kotlin/app/tivi/util/String.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | actual fun String.fmt(vararg args: Any?): String = format(*args) 7 | -------------------------------------------------------------------------------- /common/ui/screens/src/commonMain/kotlin/app/tivi/screens/Parcelize.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.screens 5 | 6 | @Target(AnnotationTarget.CLASS) 7 | @Retention(AnnotationRetention.BINARY) 8 | annotation class Parcelize 9 | -------------------------------------------------------------------------------- /compose-stability.conf: -------------------------------------------------------------------------------- 1 | // Consider all Tivi models as stable 2 | app.tivi.data.compoundmodels.* 3 | app.tivi.data.imagemodels.* 4 | app.tivi.data.models.* 5 | app.tivi.data.views.* 6 | 7 | // Consider kotlin collections stable 8 | kotlin.collections.* 9 | 10 | // Consider kotlinx.datetime models stable 11 | kotlinx.datetime.DayOfWeek 12 | kotlinx.datetime.Instant 13 | kotlinx.datetime.LocalDate 14 | kotlinx.datetime.LocalDateTime 15 | kotlinx.datetime.LocalTime 16 | kotlinx.datetime.TimeZone 17 | kotlin.time.Duration 18 | 19 | kotlinx.coroutines.CoroutineScope 20 | 21 | androidx.paging.compose.LazyPagingItems 22 | 23 | coil3.compose.AsyncImagePainter.State 24 | -------------------------------------------------------------------------------- /core/analytics/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.android.library") 7 | id("app.tivi.kotlin.multiplatform") 8 | } 9 | 10 | kotlin { 11 | sourceSets { 12 | commonMain { 13 | dependencies { 14 | implementation(projects.core.base) 15 | api(projects.core.preferences) 16 | } 17 | } 18 | 19 | androidMain { 20 | dependencies { 21 | implementation(libs.google.firebase.analytics) 22 | implementation(libs.kotlininject.runtime) 23 | } 24 | } 25 | } 26 | } 27 | 28 | android { 29 | namespace = "app.tivi.core.analytics" 30 | } 31 | -------------------------------------------------------------------------------- /core/analytics/src/androidMain/kotlin/app/tivi/core/analytics/AnalyticsPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.analytics 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface AnalyticsPlatformComponent { 10 | @ApplicationScope 11 | @Provides 12 | fun provideTiviFirebaseAnalytics(bind: TiviFirebaseAnalytics): Analytics = bind 13 | } 14 | -------------------------------------------------------------------------------- /core/analytics/src/commonMain/kotlin/app/tivi/core/analytics/Analytics.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2021, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.analytics 5 | 6 | import kotlin.experimental.ExperimentalObjCName 7 | import kotlin.native.ObjCName 8 | 9 | @OptIn(ExperimentalObjCName::class) 10 | @ObjCName(swiftName = "TiviAnalytics") 11 | interface Analytics { 12 | fun trackScreenView( 13 | name: String, 14 | arguments: Map? = null, 15 | ) 16 | 17 | fun setEnabled(enabled: Boolean) 18 | } 19 | -------------------------------------------------------------------------------- /core/analytics/src/commonMain/kotlin/app/tivi/core/analytics/AnalyticsComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.analytics 5 | 6 | import app.tivi.appinitializers.AppInitializer 7 | import me.tatarka.inject.annotations.IntoSet 8 | import me.tatarka.inject.annotations.Provides 9 | 10 | expect interface AnalyticsPlatformComponent 11 | 12 | interface AnalyticsComponent : AnalyticsPlatformComponent { 13 | @Provides 14 | @IntoSet 15 | fun provideAnalyticsInitializer(impl: AnalyticsInitializer): AppInitializer = impl 16 | } 17 | -------------------------------------------------------------------------------- /core/analytics/src/iosMain/kotlin/app/tivi/core/analytics/AnalyticsPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.analytics 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface AnalyticsPlatformComponent { 10 | @get:Provides 11 | @get:ApplicationScope 12 | val analytics: Analytics 13 | } 14 | -------------------------------------------------------------------------------- /core/analytics/src/jvmMain/kotlin/app/tivi/core/analytics/AnalyticsPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.analytics 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface AnalyticsPlatformComponent { 10 | @Provides 11 | @ApplicationScope 12 | fun provideAnalytics(): Analytics = object : Analytics { 13 | override fun trackScreenView(name: String, arguments: Map?) = Unit 14 | override fun setEnabled(enabled: Boolean) = Unit 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/base/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(libs.kotlin.coroutines.core) 14 | api(libs.kotlininject.runtime) 15 | api(libs.kermit.kermit) 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/base/src/commonMain/kotlin/app/tivi/animations/Lerp.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.animations 5 | 6 | fun lerp( 7 | startValue: Float, 8 | endValue: Float, 9 | fraction: Float, 10 | ) = startValue + fraction * (endValue - startValue) 11 | -------------------------------------------------------------------------------- /core/base/src/commonMain/kotlin/app/tivi/app/ApplicationInfo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.app 5 | 6 | data class ApplicationInfo( 7 | val packageName: String, 8 | val debugBuild: Boolean, 9 | val flavor: Flavor, 10 | val versionName: String, 11 | val versionCode: Int, 12 | val cachePath: () -> String, 13 | val platform: Platform, 14 | ) 15 | 16 | enum class Platform { 17 | IOS, 18 | ANDROID, 19 | DESKTOP, 20 | } 21 | 22 | enum class Flavor { 23 | Qa, 24 | Standard, 25 | } 26 | -------------------------------------------------------------------------------- /core/base/src/commonMain/kotlin/app/tivi/appinitializers/AppInitializer.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.appinitializers 5 | 6 | fun interface AppInitializer { 7 | fun initialize() 8 | } 9 | -------------------------------------------------------------------------------- /core/base/src/commonMain/kotlin/app/tivi/base/InvokeStatus.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.base 5 | 6 | sealed class InvokeStatus 7 | object InvokeStarted : InvokeStatus() 8 | object InvokeSuccess : InvokeStatus() 9 | data class InvokeError(val throwable: Throwable) : InvokeStatus() 10 | -------------------------------------------------------------------------------- /core/base/src/commonMain/kotlin/app/tivi/extensions/Lazy.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.extensions 5 | 6 | @Suppress("NOTHING_TO_INLINE") 7 | inline fun unsafeLazy(noinline initializer: () -> T): Lazy = lazy(LazyThreadSafetyMode.NONE, initializer) 8 | -------------------------------------------------------------------------------- /core/base/src/commonMain/kotlin/app/tivi/inject/Scopes.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | import kotlinx.coroutines.CoroutineScope 7 | import me.tatarka.inject.annotations.Scope 8 | 9 | @Scope 10 | annotation class ApplicationScope 11 | 12 | @Scope 13 | annotation class ActivityScope 14 | 15 | typealias ApplicationCoroutineScope = CoroutineScope 16 | -------------------------------------------------------------------------------- /core/base/src/commonMain/kotlin/app/tivi/util/AppCoroutineDispatchers.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | import kotlinx.coroutines.CoroutineDispatcher 7 | 8 | data class AppCoroutineDispatchers( 9 | val io: CoroutineDispatcher, 10 | val databaseWrite: CoroutineDispatcher, 11 | val databaseRead: CoroutineDispatcher, 12 | val computation: CoroutineDispatcher, 13 | val main: CoroutineDispatcher, 14 | ) 15 | -------------------------------------------------------------------------------- /core/base/src/commonMain/kotlin/app/tivi/util/Result.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | import kotlinx.coroutines.CancellationException 7 | 8 | inline fun T.cancellableRunCatching(block: T.() -> R): Result { 9 | return try { 10 | Result.success(block()) 11 | } catch (ce: CancellationException) { 12 | throw ce 13 | } catch (e: Throwable) { 14 | Result.failure(e) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/logging/src/androidMain/kotlin/app/tivi/util/CrashlyticsAndroidInitializer.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | import app.tivi.appinitializers.AppInitializer 7 | import co.touchlab.crashkios.crashlytics.enableCrashlytics 8 | import co.touchlab.kermit.Logger 9 | 10 | internal object CrashlyticsAndroidInitializer : AppInitializer { 11 | override fun initialize() { 12 | enableCrashlytics() 13 | 14 | // Add Crashlytics log writer 15 | Logger.addLogWriter(CrashlyticsLoggerWriter()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/logging/src/androidMain/kotlin/app/tivi/util/LoggerPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | import app.tivi.appinitializers.AppInitializer 7 | import me.tatarka.inject.annotations.IntoSet 8 | import me.tatarka.inject.annotations.Provides 9 | 10 | actual interface LoggerPlatformComponent { 11 | @Provides 12 | @IntoSet 13 | fun provideCrashlyticsAndroidInitializer(): AppInitializer = CrashlyticsAndroidInitializer 14 | 15 | @Provides 16 | fun bindSetCrashReportingEnabledAction(): SetCrashReportingEnabledAction { 17 | return AndroidSetCrashReportingEnabledAction 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/logging/src/commonMain/kotlin/app/tivi/util/LoggerComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | import app.tivi.appinitializers.AppInitializer 7 | import me.tatarka.inject.annotations.IntoSet 8 | import me.tatarka.inject.annotations.Provides 9 | 10 | expect interface LoggerPlatformComponent 11 | 12 | interface LoggerComponent : LoggerPlatformComponent { 13 | @Provides 14 | @IntoSet 15 | fun provideCrashReportingInitializer(impl: CrashReportingInitializer): AppInitializer = impl 16 | 17 | @Provides 18 | @IntoSet 19 | fun provideKermitInitializer(impl: KermitInitializer): AppInitializer = impl 20 | } 21 | -------------------------------------------------------------------------------- /core/logging/src/commonMain/kotlin/app/tivi/util/SetCrashReportingEnabledAction.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | fun interface SetCrashReportingEnabledAction { 7 | operator fun invoke(enabled: Boolean) 8 | } 9 | -------------------------------------------------------------------------------- /core/logging/src/iosMain/kotlin/app/tivi/util/LoggerPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | import app.tivi.appinitializers.AppInitializer 7 | import me.tatarka.inject.annotations.IntoSet 8 | import me.tatarka.inject.annotations.Provides 9 | 10 | actual interface LoggerPlatformComponent { 11 | @get:Provides 12 | val setCrashReportingEnabledAction: SetCrashReportingEnabledAction 13 | 14 | @Provides 15 | @IntoSet 16 | fun provideCrashlyticsIosInitializer(): AppInitializer = CrashlyticsIosInitializer 17 | } 18 | -------------------------------------------------------------------------------- /core/logging/src/jvmMain/kotlin/app/tivi/util/LoggerPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | import me.tatarka.inject.annotations.Provides 7 | 8 | actual interface LoggerPlatformComponent { 9 | @Provides 10 | fun bindSetCrashReportingEnabledAction(): SetCrashReportingEnabledAction { 11 | return NoopSetCrashReportingEnabledAction 12 | } 13 | } 14 | 15 | private object NoopSetCrashReportingEnabledAction : SetCrashReportingEnabledAction { 16 | override fun invoke(enabled: Boolean) {} 17 | } 18 | -------------------------------------------------------------------------------- /core/notifications/core/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /core/notifications/core/src/androidMain/kotlin/app/tivi/core/notifications/NotificationPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.notifications 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface NotificationPlatformComponent { 10 | 11 | @Provides 12 | @ApplicationScope 13 | fun provideNotificationManager(impl: AndroidNotificationManager): NotificationManager = impl 14 | } 15 | -------------------------------------------------------------------------------- /core/notifications/core/src/commonMain/kotlin/app/tivi/core/notifications/NotificationsComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.notifications 5 | 6 | expect interface NotificationPlatformComponent 7 | 8 | interface NotificationsComponent : NotificationPlatformComponent 9 | -------------------------------------------------------------------------------- /core/notifications/core/src/iosMain/kotlin/app/tivi/core/notifications/NotificationPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.notifications 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface NotificationPlatformComponent { 10 | 11 | @Provides 12 | @ApplicationScope 13 | fun provideNotificationManager(impl: IosNotificationManager): NotificationManager = impl 14 | } 15 | -------------------------------------------------------------------------------- /core/notifications/core/src/jvmMain/kotlin/app/tivi/core/notifications/EmptyNotificationManager.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.notifications 5 | 6 | internal object EmptyNotificationManager : NotificationManager 7 | -------------------------------------------------------------------------------- /core/notifications/core/src/jvmMain/kotlin/app/tivi/core/notifications/NotificationPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.notifications 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface NotificationPlatformComponent { 10 | 11 | @Provides 12 | @ApplicationScope 13 | fun provideNotificationManager(): NotificationManager = EmptyNotificationManager 14 | } 15 | -------------------------------------------------------------------------------- /core/notifications/protos/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.android.library") 7 | id("app.tivi.kotlin.multiplatform") 8 | alias(libs.plugins.wire) 9 | } 10 | 11 | kotlin { 12 | sourceSets { 13 | commonMain { 14 | dependencies { 15 | api(projects.core.base) 16 | api(libs.wire.runtime) 17 | } 18 | } 19 | } 20 | } 21 | 22 | wire { 23 | kotlin {} 24 | } 25 | 26 | android { 27 | namespace = "app.tivi.core.notifications.proto" 28 | } 29 | -------------------------------------------------------------------------------- /core/notifications/protos/src/commonMain/proto/pending.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/protobuf/timestamp.proto"; 4 | 5 | package app.tivi.core.notifications.proto; 6 | 7 | message PendingNotification { 8 | string id = 1; 9 | string title = 2; 10 | string message = 3; 11 | string channel_id = 4; 12 | optional string deeplink_url = 5; 13 | optional google.protobuf.Timestamp date = 6; 14 | } 15 | 16 | message PendingNotifications { 17 | repeated PendingNotification pending = 1; 18 | } 19 | -------------------------------------------------------------------------------- /core/performance/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.android.library") 7 | id("app.tivi.kotlin.multiplatform") 8 | } 9 | 10 | kotlin { 11 | sourceSets { 12 | commonMain { 13 | dependencies { 14 | implementation(projects.core.base) 15 | implementation(libs.kotlininject.runtime) 16 | } 17 | } 18 | 19 | androidMain { 20 | dependencies { 21 | implementation(libs.google.firebase.perf) 22 | } 23 | } 24 | } 25 | } 26 | 27 | android { 28 | namespace = "app.tivi.core.perf" 29 | } 30 | -------------------------------------------------------------------------------- /core/performance/src/androidMain/kotlin/app/tivi/core/perf/PerformanceComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.perf 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface PerformanceComponent { 10 | @ApplicationScope 11 | @Provides 12 | fun provideTracer(bind: AndroidTracer): Tracer = bind 13 | } 14 | -------------------------------------------------------------------------------- /core/performance/src/commonMain/kotlin/app/tivi/core/perf/PerformanceComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.perf 5 | 6 | expect interface PerformanceComponent 7 | -------------------------------------------------------------------------------- /core/performance/src/commonMain/kotlin/app/tivi/core/perf/Tracer.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2021, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.perf 5 | 6 | fun interface Tracer { 7 | fun trace( 8 | name: String, 9 | block: () -> Unit, 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /core/performance/src/iosMain/kotlin/app/tivi/core/perf/PerformanceComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.perf 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface PerformanceComponent { 10 | @ApplicationScope 11 | @Provides 12 | fun provideTracer(): Tracer = Tracer { _, block -> block() } 13 | } 14 | -------------------------------------------------------------------------------- /core/performance/src/jvmMain/kotlin/app/tivi/core/perf/PerformanceComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.perf 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface PerformanceComponent { 10 | @ApplicationScope 11 | @Provides 12 | fun provideTracer(): Tracer = Tracer { _, block -> block() } 13 | } 14 | -------------------------------------------------------------------------------- /core/permissions/src/androidMain/kotlin/app/tivi/core/permissions/PermissionsController.android.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.permissions 5 | 6 | import androidx.activity.ComponentActivity 7 | 8 | fun PermissionsController.bind(activity: ComponentActivity) { 9 | if (this is MokoPermissionControllerWrapper) { 10 | mokoPermissionController.bind(activity) 11 | } else { 12 | error("PermissionsController does not wrap Moko Permissions") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/permissions/src/androidMain/kotlin/app/tivi/core/permissions/PermissionsPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.permissions 5 | 6 | import android.app.Application 7 | import app.tivi.inject.ApplicationScope 8 | import me.tatarka.inject.annotations.Provides 9 | 10 | actual interface PermissionsPlatformComponent { 11 | @Provides 12 | @ApplicationScope 13 | fun providePermissionController(application: Application): PermissionsController { 14 | return MokoPermissionControllerWrapper( 15 | mokoPermissionController = dev.icerock.moko.permissions.PermissionsController(application), 16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/permissions/src/commonMain/kotlin/app/tivi/core/permissions/PermissionsComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.permissions 5 | 6 | expect interface PermissionsPlatformComponent 7 | 8 | interface PermissionsComponent : PermissionsPlatformComponent 9 | -------------------------------------------------------------------------------- /core/permissions/src/iosMain/kotlin/app/tivi/core/permissions/PermissionsPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.core.permissions 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface PermissionsPlatformComponent { 10 | @Provides 11 | @ApplicationScope 12 | fun providePermissionController(): PermissionsController { 13 | return MokoPermissionControllerWrapper( 14 | mokoPermissionController = dev.icerock.moko.permissions.ios.PermissionsController(), 15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/powercontroller/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.android.library") 7 | id("app.tivi.kotlin.multiplatform") 8 | } 9 | 10 | kotlin { 11 | sourceSets { 12 | commonMain { 13 | dependencies { 14 | implementation(projects.core.base) 15 | api(projects.core.preferences) 16 | } 17 | } 18 | 19 | androidMain { 20 | dependencies { 21 | implementation(libs.androidx.core) 22 | 23 | implementation(libs.kotlininject.runtime) 24 | } 25 | } 26 | } 27 | } 28 | 29 | android { 30 | namespace = "app.tivi.core.powercontroller" 31 | } 32 | -------------------------------------------------------------------------------- /core/powercontroller/src/androidMain/kotlin/app/tivi/util/PowerControllerComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface PowerControllerComponent { 10 | @Provides 11 | @ApplicationScope 12 | fun providePowerController(bind: AndroidPowerController): PowerController = bind 13 | } 14 | -------------------------------------------------------------------------------- /core/powercontroller/src/commonMain/kotlin/app/tivi/util/PowerControllerComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | expect interface PowerControllerComponent 7 | -------------------------------------------------------------------------------- /core/powercontroller/src/iosMain/kotlin/app/tivi/util/PowerControllerComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface PowerControllerComponent { 10 | @Provides 11 | @ApplicationScope 12 | fun providePowerController(bind: DefaultPowerController): PowerController = bind 13 | } 14 | -------------------------------------------------------------------------------- /core/powercontroller/src/jvmMain/kotlin/app/tivi/util/PowerControllerComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.util 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface PowerControllerComponent { 10 | @Provides 11 | @ApplicationScope 12 | fun providePowerController(bind: DefaultPowerController): PowerController = bind 13 | } 14 | -------------------------------------------------------------------------------- /core/preferences/src/commonMain/kotlin/app/tivi/settings/Preference.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.settings 5 | 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | interface Preference { 9 | val defaultValue: T 10 | 11 | val flow: Flow 12 | suspend fun set(value: T) 13 | suspend fun get(): T 14 | } 15 | 16 | suspend fun Preference.toggle() = set(!get()) 17 | -------------------------------------------------------------------------------- /core/preferences/src/commonMain/kotlin/app/tivi/settings/PreferencesComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.settings 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | expect interface PreferencesPlatformComponent 10 | 11 | interface PreferencesComponent : PreferencesPlatformComponent { 12 | val preferences: TiviPreferences 13 | 14 | @ApplicationScope 15 | @Provides 16 | fun providePreferences(bind: TiviPreferencesImpl): TiviPreferences = bind 17 | } 18 | -------------------------------------------------------------------------------- /core/preferences/src/iosMain/kotlin/app/tivi/settings/PreferencesPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.settings 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import com.russhwolf.settings.NSUserDefaultsSettings 8 | import com.russhwolf.settings.ObservableSettings 9 | import me.tatarka.inject.annotations.Provides 10 | import platform.Foundation.NSUserDefaults 11 | 12 | actual interface PreferencesPlatformComponent { 13 | @ApplicationScope 14 | @Provides 15 | fun provideSettings(delegate: NSUserDefaults): ObservableSettings = NSUserDefaultsSettings(delegate) 16 | } 17 | -------------------------------------------------------------------------------- /core/preferences/src/jvmMain/kotlin/app/tivi/settings/PreferencesPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.settings 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import com.russhwolf.settings.ObservableSettings 8 | import com.russhwolf.settings.PreferencesSettings 9 | import java.util.prefs.Preferences 10 | import me.tatarka.inject.annotations.Provides 11 | 12 | actual interface PreferencesPlatformComponent { 13 | @ApplicationScope 14 | @Provides 15 | fun provideSettings(delegate: Preferences): ObservableSettings = PreferencesSettings(delegate) 16 | } 17 | -------------------------------------------------------------------------------- /data/anticipatedshows/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.data.models) 14 | implementation(projects.data.db) 15 | implementation(projects.data.legacy) // remove this eventually 16 | 17 | implementation(projects.api.trakt) 18 | 19 | api(libs.store) 20 | implementation(libs.kotlinx.atomicfu) 21 | 22 | implementation(libs.kotlininject.runtime) 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /data/anticipatedshows/src/commonMain/kotlin/app/tivi/data/anticipatedshows/AnticipatedShowsBinds.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.anticipatedshows 5 | 6 | import me.tatarka.inject.annotations.Provides 7 | 8 | interface AnticipatedShowsBinds { 9 | @Provides 10 | fun provideTraktAnticipatedShowsDataSource( 11 | bind: TraktAnticipatedShowsDataSource, 12 | ): AnticipatedShowsDataSource = bind 13 | } 14 | -------------------------------------------------------------------------------- /data/anticipatedshows/src/commonMain/kotlin/app/tivi/data/anticipatedshows/AnticipatedShowsDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.anticipatedshows 5 | 6 | import app.tivi.data.models.AnticipatedShowEntry 7 | import app.tivi.data.models.TiviShow 8 | 9 | fun interface AnticipatedShowsDataSource { 10 | suspend operator fun invoke( 11 | page: Int, 12 | pageSize: Int, 13 | ): List> 14 | } 15 | -------------------------------------------------------------------------------- /data/anticipatedshows/src/commonMain/kotlin/app/tivi/data/anticipatedshows/AnticipatedShowsLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.anticipatedshows 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.GroupLastRequestStore 8 | import app.tivi.data.models.Request 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class AnticipatedShowsLastRequestStore( 13 | dao: LastRequestDao, 14 | ) : GroupLastRequestStore(Request.ANTICIPATED_SHOWS, dao) 15 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/SqlDelightTransactionRunner.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data 5 | 6 | import app.tivi.data.db.DatabaseTransactionRunner 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | class SqlDelightTransactionRunner(private val db: Database) : DatabaseTransactionRunner { 11 | override fun invoke(block: () -> T): T { 12 | return db.transactionWithResult { 13 | block() 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/Utils.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data 5 | 6 | internal val Boolean.sqlValue: Long get() = if (this) 1 else 0 7 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/columnadaptors/ImageTypeColumnAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.columnadaptors 5 | 6 | import app.cash.sqldelight.ColumnAdapter 7 | import app.tivi.data.models.ImageType 8 | 9 | internal object ImageTypeColumnAdapter : ColumnAdapter { 10 | override fun decode(databaseValue: String): ImageType { 11 | return ImageType.entries.first { it.storageKey == databaseValue } 12 | } 13 | 14 | override fun encode(value: ImageType): String = value.storageKey 15 | } 16 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/columnadaptors/InstantLongColumnAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.columnadaptors 5 | 6 | import app.cash.sqldelight.ColumnAdapter 7 | import kotlinx.datetime.Instant 8 | 9 | internal object InstantLongColumnAdapter : ColumnAdapter { 10 | override fun decode(databaseValue: Long): Instant = Instant.fromEpochMilliseconds(databaseValue) 11 | override fun encode(value: Instant): Long = value.toEpochMilliseconds() 12 | } 13 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/columnadaptors/InstantStringColumnAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.columnadaptors 5 | 6 | import app.cash.sqldelight.ColumnAdapter 7 | import kotlinx.datetime.Instant 8 | 9 | internal object InstantStringColumnAdapter : ColumnAdapter { 10 | override fun decode(databaseValue: String): Instant = Instant.parse(databaseValue) 11 | override fun encode(value: Instant): String = value.toString() 12 | } 13 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/columnadaptors/LocalDateColumnAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.columnadaptors 5 | 6 | import app.cash.sqldelight.ColumnAdapter 7 | import kotlinx.datetime.LocalDate 8 | 9 | internal object LocalDateColumnAdapter : ColumnAdapter { 10 | override fun decode(databaseValue: String): LocalDate = LocalDate.parse(databaseValue) 11 | override fun encode(value: LocalDate): String = value.toString() 12 | } 13 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/columnadaptors/LocalDateTimeColumnAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.columnadaptors 5 | 6 | import app.cash.sqldelight.ColumnAdapter 7 | import kotlinx.datetime.LocalDateTime 8 | 9 | internal object LocalDateTimeColumnAdapter : ColumnAdapter { 10 | override fun decode(databaseValue: String): LocalDateTime = LocalDateTime.parse(databaseValue) 11 | override fun encode(value: LocalDateTime): String = value.toString() 12 | } 13 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/columnadaptors/LocalTimeColumnAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.columnadaptors 5 | 6 | import app.cash.sqldelight.ColumnAdapter 7 | import kotlinx.datetime.LocalTime 8 | 9 | internal object LocalTimeColumnAdapter : ColumnAdapter { 10 | override fun decode(databaseValue: String): LocalTime = LocalTime.parse(databaseValue) 11 | override fun encode(value: LocalTime): String = value.toString() 12 | } 13 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/columnadaptors/PendingActionColumnAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.columnadaptors 5 | 6 | import app.cash.sqldelight.ColumnAdapter 7 | import app.tivi.data.models.PendingAction 8 | 9 | internal object PendingActionColumnAdapter : ColumnAdapter { 10 | override fun decode(databaseValue: String): PendingAction { 11 | return PendingAction.entries.first { it.value == databaseValue } 12 | } 13 | 14 | override fun encode(value: PendingAction): String = value.value 15 | } 16 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/columnadaptors/RequestColumnAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.columnadaptors 5 | 6 | import app.cash.sqldelight.ColumnAdapter 7 | import app.tivi.data.models.Request 8 | 9 | internal object RequestColumnAdapter : ColumnAdapter { 10 | override fun decode(databaseValue: String): Request { 11 | return Request.entries.first { it.tag == databaseValue } 12 | } 13 | 14 | override fun encode(value: Request): String = value.tag 15 | } 16 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/columnadaptors/ShowStatusColumnAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.columnadaptors 5 | 6 | import app.cash.sqldelight.ColumnAdapter 7 | import app.tivi.data.models.ShowStatus 8 | 9 | internal object ShowStatusColumnAdapter : ColumnAdapter { 10 | override fun decode(databaseValue: String): ShowStatus { 11 | return ShowStatus.entries.first { it.storageKey == databaseValue } 12 | } 13 | 14 | override fun encode(value: ShowStatus): String = value.storageKey 15 | } 16 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/columnadaptors/TimeZoneColumnAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.columnadaptors 5 | 6 | import app.cash.sqldelight.ColumnAdapter 7 | import kotlinx.datetime.TimeZone 8 | 9 | internal object TimeZoneColumnAdapter : ColumnAdapter { 10 | override fun decode(databaseValue: String): TimeZone = TimeZone.of(databaseValue) 11 | override fun encode(value: TimeZone): String = value.id 12 | } 13 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/kotlin/app/tivi/data/daos/SqlDelightEntityDao.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.daos 5 | 6 | import app.tivi.data.Database 7 | import app.tivi.data.models.TiviEntity 8 | 9 | interface SqlDelightEntityDao : EntityDao { 10 | val db: Database 11 | 12 | override fun insert(entities: List) { 13 | db.transaction { 14 | for (entity in entities) { 15 | insert(entity) 16 | } 17 | } 18 | } 19 | 20 | override fun upsert(entities: List) { 21 | db.transaction { 22 | for (entity in entities) { 23 | upsert(entity) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/sqldelight/app/tivi/data/shows_last_watched.sq: -------------------------------------------------------------------------------- 1 | CREATE VIEW `shows_last_watched` AS SELECT 2 | shows.id AS show_id, 3 | s.id AS season_id, 4 | eps.id AS episode_id, 5 | MAX((1000 * s.number) + eps.number) AS last_watched_abs_number 6 | FROM shows 7 | INNER JOIN seasons AS s ON shows.id = s.show_id 8 | INNER JOIN episodes AS eps ON eps.season_id = s.id 9 | INNER JOIN episode_watch_entries AS ew ON ew.episode_id = eps.id 10 | WHERE 11 | s.number != 0 12 | AND s.ignored = 0 13 | GROUP BY shows.id 14 | ORDER BY ew.watched_at DESC; 15 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/sqldelight/app/tivi/data/shows_view_watch_stats.sq: -------------------------------------------------------------------------------- 1 | CREATE VIEW `shows_view_watch_stats` AS 2 | SELECT shows.id AS show_id, 3 | COUNT(*) AS episode_count, 4 | COUNT(ew.watched_at) AS watched_episode_count 5 | FROM shows 6 | INNER JOIN seasons AS s ON shows.id = s.show_id 7 | INNER JOIN episodes AS eps ON eps.season_id = s.id 8 | LEFT JOIN episode_watch_entries AS ew ON ew.episode_id = eps.id 9 | WHERE eps.first_aired IS NOT NULL 10 | AND datetime(eps.first_aired) < datetime('now') 11 | AND s.number != 0 12 | AND s.ignored = 0 13 | GROUP BY shows.id; 14 | 15 | -- queries 16 | 17 | watchStatsForShowId: 18 | SELECT * FROM shows_view_watch_stats 19 | WHERE show_id = :showId; 20 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/sqldelight/migrations/32.sqm: -------------------------------------------------------------------------------- 1 | -- drop FTS table (for existing Room generated DBs) 2 | 3 | DROP TRIGGER IF EXISTS room_fts_content_sync_shows_fts_BEFORE_UPDATE; 4 | DROP TRIGGER IF EXISTS room_fts_content_sync_shows_fts_BEFORE_DELETE; 5 | DROP TRIGGER IF EXISTS room_fts_content_sync_shows_fts_AFTER_UPDATE; 6 | DROP TRIGGER IF EXISTS room_fts_content_sync_shows_fts_AFTER_INSERT; 7 | DROP TABLE IF EXISTS `shows_fts`; 8 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/sqldelight/migrations/33.sqm: -------------------------------------------------------------------------------- 1 | 2 | -- delete duplicate related shows 3 | DELETE FROM `related_shows` WHERE rowid NOT IN ( 4 | SELECT MIN(rowid) 5 | FROM related_shows 6 | GROUP BY show_id, other_show_id 7 | ); 8 | 9 | -- finally add an index to make sure this doesn't happen in the future 10 | CREATE UNIQUE INDEX IF NOT EXISTS `index_related_shows_unique` ON `related_shows` (`show_id`, `other_show_id`); 11 | -------------------------------------------------------------------------------- /data/db-sqldelight/src/commonMain/sqldelight/migrations/34.sqm: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS `anticipated_shows` ( 2 | `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 3 | `show_id` INTEGER NOT NULL, 4 | `page` INTEGER AS kotlin.Int NOT NULL, 5 | `page_order` INTEGER AS kotlin.Int NOT NULL, 6 | FOREIGN KEY(`show_id`) REFERENCES `shows`(`id`) ON UPDATE CASCADE ON DELETE CASCADE 7 | ); 8 | -------------------------------------------------------------------------------- /data/db/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | implementation(projects.core.base) 14 | api(projects.data.models) 15 | api(libs.paging.common) 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data/db/src/commonMain/kotlin/app/tivi/data/daos/EntryDao.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.daos 5 | 6 | import app.tivi.data.compoundmodels.EntryWithShow 7 | import app.tivi.data.models.Entry 8 | 9 | /** 10 | * This interface represents a DAO which contains entities which are part of a single collective list. 11 | */ 12 | interface EntryDao> : EntityDao { 13 | fun deleteAll() 14 | } 15 | -------------------------------------------------------------------------------- /data/db/src/commonMain/kotlin/app/tivi/data/daos/LastRequestDao.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.daos 5 | 6 | import app.tivi.data.models.LastRequest 7 | import app.tivi.data.models.Request 8 | 9 | interface LastRequestDao : EntityDao { 10 | 11 | fun lastRequest(request: Request, entityId: Long): LastRequest? 12 | 13 | fun requestCount(request: Request, entityId: Long): Int 14 | } 15 | -------------------------------------------------------------------------------- /data/db/src/commonMain/kotlin/app/tivi/data/daos/LibraryShowsDao.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.daos 5 | 6 | import androidx.paging.PagingSource 7 | import app.tivi.data.compoundmodels.LibraryShow 8 | import app.tivi.data.models.SortOption 9 | 10 | interface LibraryShowsDao { 11 | fun pagedListLastWatched( 12 | sort: SortOption, 13 | filter: String?, 14 | onlyFollowed: Boolean, 15 | ): PagingSource 16 | } 17 | -------------------------------------------------------------------------------- /data/db/src/commonMain/kotlin/app/tivi/data/daos/PaginatedEntryDao.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.daos 5 | 6 | import app.tivi.data.compoundmodels.EntryWithShow 7 | import app.tivi.data.models.PaginatedEntry 8 | 9 | interface PaginatedEntryDao> : EntryDao { 10 | fun deletePage(page: Int) 11 | fun getLastPage(): Int? 12 | } 13 | 14 | fun > PaginatedEntryDao.updatePage( 15 | page: Int, 16 | entities: List, 17 | ) { 18 | deletePage(page) 19 | upsert(entities) 20 | } 21 | -------------------------------------------------------------------------------- /data/db/src/commonMain/kotlin/app/tivi/data/daos/PairEntryDao.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.daos 5 | 6 | import app.tivi.data.compoundmodels.EntryWithShow 7 | import app.tivi.data.models.MultipleEntry 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | /** 11 | * This interface represents a DAO which contains entities which are part of a collective list for a given show. 12 | */ 13 | interface PairEntryDao> : EntityDao { 14 | fun entriesWithShowsObservable(showId: Long): Flow> 15 | fun deleteWithShowId(showId: Long) 16 | } 17 | -------------------------------------------------------------------------------- /data/db/src/commonMain/kotlin/app/tivi/data/daos/RelatedShowsDao.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.daos 5 | 6 | import app.tivi.data.compoundmodels.RelatedShowEntryWithShow 7 | import app.tivi.data.models.RelatedShowEntry 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | interface RelatedShowsDao : PairEntryDao { 11 | fun entriesObservable(showId: Long): Flow> 12 | fun deleteAll() 13 | } 14 | -------------------------------------------------------------------------------- /data/db/src/commonMain/kotlin/app/tivi/data/daos/ShowFtsDao.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.daos 5 | 6 | import app.tivi.data.models.TiviShow 7 | 8 | interface ShowFtsDao { 9 | 10 | fun search(filter: String): List 11 | } 12 | -------------------------------------------------------------------------------- /data/db/src/commonMain/kotlin/app/tivi/data/daos/UserDao.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.daos 5 | 6 | import app.tivi.data.models.TraktUser 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | interface UserDao : EntityDao { 10 | 11 | fun observeMe(): Flow 12 | 13 | fun observeTraktUser(username: String): Flow 14 | 15 | fun getUser(username: String): TraktUser? 16 | 17 | fun getId(username: String): Long? 18 | 19 | fun deleteWithUsername(username: String) 20 | 21 | fun deleteMe() 22 | 23 | fun deleteAll() 24 | } 25 | -------------------------------------------------------------------------------- /data/db/src/commonMain/kotlin/app/tivi/data/db/DatabaseTransactionRunner.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.db 5 | 6 | interface DatabaseTransactionRunner { 7 | operator fun invoke(block: () -> T): T 8 | } 9 | -------------------------------------------------------------------------------- /data/episodes/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.core.preferences) 14 | api(projects.data.models) 15 | api(projects.data.traktauth) 16 | implementation(projects.data.db) 17 | implementation(projects.data.legacy) // remove this eventually 18 | 19 | api(projects.api.trakt) 20 | api(projects.api.tmdb) 21 | 22 | implementation(libs.kotlininject.runtime) 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /data/episodes/src/commonMain/kotlin/app/tivi/data/episodes/EpisodeLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.episodes 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.EntityLastRequestStore 8 | import app.tivi.data.models.Request 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class EpisodeLastRequestStore( 13 | dao: LastRequestDao, 14 | ) : EntityLastRequestStore(Request.EPISODE_DETAILS, dao) 15 | -------------------------------------------------------------------------------- /data/episodes/src/commonMain/kotlin/app/tivi/data/episodes/EpisodeWatchLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.episodes 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.EntityLastRequestStore 8 | import app.tivi.data.models.Request 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class EpisodeWatchLastRequestStore( 13 | dao: LastRequestDao, 14 | ) : EntityLastRequestStore(Request.SHOW_EPISODE_WATCHES, dao) 15 | -------------------------------------------------------------------------------- /data/episodes/src/commonMain/kotlin/app/tivi/data/episodes/SeasonLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.episodes 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.EntityLastRequestStore 8 | import app.tivi.data.models.Request 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class SeasonLastRequestStore( 13 | dao: LastRequestDao, 14 | ) : EntityLastRequestStore(Request.SEASON_DETAILS, dao) 15 | -------------------------------------------------------------------------------- /data/episodes/src/commonMain/kotlin/app/tivi/data/episodes/ShowSeasonsLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.episodes 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.EntityLastRequestStore 8 | import app.tivi.data.models.Request 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class ShowSeasonsLastRequestStore( 13 | dao: LastRequestDao, 14 | ) : EntityLastRequestStore(Request.SHOW_SEASONS, dao) 15 | -------------------------------------------------------------------------------- /data/followedshows/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.data.models) 14 | api(projects.data.traktauth) 15 | implementation(projects.data.db) 16 | implementation(projects.data.legacy) // remove this eventually 17 | 18 | api(projects.api.trakt) 19 | api(projects.api.tmdb) 20 | 21 | api(libs.store) 22 | implementation(libs.kotlinx.atomicfu) 23 | 24 | implementation(libs.kotlininject.runtime) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /data/followedshows/src/commonMain/kotlin/app/tivi/data/followedshows/FollowedShowsBinds.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.followedshows 5 | 6 | import me.tatarka.inject.annotations.Provides 7 | 8 | interface FollowedShowsBinds { 9 | @Provides 10 | fun provideTraktFollowedShowsDataSource( 11 | bind: TraktFollowedShowsDataSource, 12 | ): FollowedShowsDataSource = bind 13 | } 14 | -------------------------------------------------------------------------------- /data/followedshows/src/commonMain/kotlin/app/tivi/data/followedshows/FollowedShowsDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.followedshows 5 | 6 | import app.moviebase.trakt.model.TraktList 7 | import app.tivi.data.models.FollowedShowEntry 8 | import app.tivi.data.models.TiviShow 9 | 10 | interface FollowedShowsDataSource { 11 | suspend fun getListShows(listId: Int): List> 12 | suspend fun addShowIdsToList(listId: Int, shows: List) 13 | suspend fun removeShowIdsFromList(listId: Int, shows: List) 14 | suspend fun getFollowedListId(): TraktList 15 | } 16 | -------------------------------------------------------------------------------- /data/followedshows/src/commonMain/kotlin/app/tivi/data/followedshows/FollowedShowsLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.followedshows 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.GroupLastRequestStore 8 | import app.tivi.data.models.Request 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class FollowedShowsLastRequestStore( 13 | dao: LastRequestDao, 14 | ) : GroupLastRequestStore(Request.FOLLOWED_SHOWS, dao) 15 | -------------------------------------------------------------------------------- /data/legacy/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.core.base) 14 | api(projects.api.trakt) 15 | api(projects.api.tmdb) 16 | api(projects.data.models) 17 | implementation(projects.data.db) 18 | 19 | api(libs.store) 20 | 21 | implementation(libs.kotlininject.runtime) 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/EpisodeIdToTraktIdMapper.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.tivi.data.daos.EpisodesDao 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | class EpisodeIdToTraktIdMapper( 11 | private val dao: EpisodesDao, 12 | ) : Mapper { 13 | override fun map(from: Long): Int { 14 | return dao.episodeTraktIdForId(from) 15 | ?: throw IllegalArgumentException("Episode with id $from does not exist") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/Mapper.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | fun interface Mapper { 7 | fun map(from: F): T 8 | } 9 | 10 | fun interface IndexedMapper { 11 | fun map(index: Int, from: F): T 12 | } 13 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/SeasonIdToTraktIdMapper.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.tivi.data.daos.SeasonsDao 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | class SeasonIdToTraktIdMapper( 11 | private val dao: SeasonsDao, 12 | ) : Mapper { 13 | override fun map(from: Long): Int { 14 | return dao.traktIdForId(from) 15 | ?: throw IllegalArgumentException("Trakt Id for season id $from does not exist") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/ShowIdToTmdbIdMapper.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.tivi.data.daos.TiviShowDao 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | class ShowIdToTmdbIdMapper( 11 | private val showDao: TiviShowDao, 12 | ) : Mapper { 13 | override fun map(from: Long) = showDao.getTmdbIdForShowId(from) 14 | } 15 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/ShowIdToTraktIdMapper.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.tivi.data.daos.TiviShowDao 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | class ShowIdToTraktIdMapper( 11 | private val showDao: TiviShowDao, 12 | ) : Mapper { 13 | override fun map(from: Long): Int? { 14 | return showDao.getTraktIdForShowId(from) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/ShowIdToTraktOrImdbIdMapper.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.tivi.data.daos.TiviShowDao 7 | import app.tivi.data.db.DatabaseTransactionRunner 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class ShowIdToTraktOrImdbIdMapper( 12 | private val showDao: TiviShowDao, 13 | private val transactionRunner: DatabaseTransactionRunner, 14 | ) : Mapper { 15 | override fun map(from: Long): String? = transactionRunner { 16 | showDao.getTraktIdForShowId(from)?.toString() 17 | ?: showDao.getImdbIdForShowId(from) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/TmdbEpisodeDetailToEpisode.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.moviebase.tmdb.model.TmdbEpisodeDetail 7 | import app.tivi.data.models.Episode 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class TmdbEpisodeDetailToEpisode : Mapper { 12 | override fun map(from: TmdbEpisodeDetail): Episode = Episode( 13 | seasonId = 0, 14 | tmdbId = from.id, 15 | title = from.name, 16 | number = from.episodeNumber, 17 | summary = from.overview, 18 | tmdbBackdropPath = from.stillPath, 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/TmdbEpisodeToEpisode.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.moviebase.tmdb.model.TmdbEpisode 7 | import app.tivi.data.models.Episode 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class TmdbEpisodeToEpisode : Mapper { 12 | override fun map(from: TmdbEpisode): Episode = Episode( 13 | seasonId = 0, 14 | tmdbId = from.id, 15 | title = from.name, 16 | number = from.episodeNumber, 17 | summary = from.overview, 18 | tmdbBackdropPath = from.stillPath, 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/TmdbSeasonToSeason.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.moviebase.tmdb.model.TmdbSeason 7 | import app.tivi.data.models.Season 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class TmdbSeasonToSeason : Mapper { 12 | override fun map(from: TmdbSeason) = Season( 13 | showId = 0, 14 | tmdbId = from.id, 15 | number = from.seasonNumber, 16 | title = from.name, 17 | summary = from.overview, 18 | episodeCount = from.episodeCount, 19 | tmdbPosterPath = from.posterPath, 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/TmdbShowToTiviShow.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.moviebase.tmdb.model.TmdbShow 7 | import app.tivi.data.models.TiviShow 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class TmdbShowToTiviShow : Mapper { 12 | override fun map(from: TmdbShow) = TiviShow( 13 | tmdbId = from.id, 14 | title = from.name, 15 | summary = from.overview, 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/TraktHistoryEntryToEpisode.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.moviebase.trakt.model.TraktHistoryItem 7 | import app.tivi.data.models.Episode 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class TraktHistoryEntryToEpisode( 12 | private val mapper: TraktEpisodeToEpisode, 13 | ) : Mapper { 14 | 15 | override fun map(from: TraktHistoryItem) = mapper.map(requireNotNull(from.episode)) 16 | } 17 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/TraktHistoryItemToEpisodeWatchEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.moviebase.trakt.model.TraktHistoryItem 7 | import app.tivi.data.models.EpisodeWatchEntry 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class TraktHistoryItemToEpisodeWatchEntry : Mapper { 12 | override fun map(from: TraktHistoryItem) = EpisodeWatchEntry( 13 | episodeId = 0, 14 | traktId = from.id?.toLong(), 15 | watchedAt = requireNotNull(from.watchedAt), 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/TraktListItemToFollowedShowEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.moviebase.trakt.model.TraktUserListItem 7 | import app.tivi.data.models.FollowedShowEntry 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class TraktListItemToFollowedShowEntry : Mapper { 12 | override fun map(from: TraktUserListItem) = FollowedShowEntry( 13 | showId = 0, 14 | followedAt = from.listedAt, 15 | traktId = from.id, 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/TraktListItemToTiviShow.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.moviebase.trakt.model.TraktUserListItem 7 | import app.tivi.data.models.TiviShow 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class TraktListItemToTiviShow( 12 | private val showMapper: TraktShowToTiviShow, 13 | ) : Mapper { 14 | override fun map(from: TraktUserListItem) = showMapper.map(from.show!!) 15 | } 16 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/TraktTrendingShowToTiviShow.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.moviebase.trakt.model.TraktTrendingShow 7 | import app.tivi.data.models.TiviShow 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class TraktTrendingShowToTiviShow( 12 | private val showMapper: TraktShowToTiviShow, 13 | ) : Mapper { 14 | override fun map(from: TraktTrendingShow) = showMapper.map(from.show!!) 15 | } 16 | -------------------------------------------------------------------------------- /data/legacy/src/commonMain/kotlin/app/tivi/data/mappers/TraktTrendingShowToTrendingShowEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2020, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.mappers 5 | 6 | import app.moviebase.trakt.model.TraktTrendingShow 7 | import app.tivi.data.models.TrendingShowEntry 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class TraktTrendingShowToTrendingShowEntry : Mapper { 12 | 13 | override fun map(from: TraktTrendingShow): TrendingShowEntry { 14 | return TrendingShowEntry( 15 | showId = 0, 16 | watchers = from.watchers ?: 0, 17 | page = 0, 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /data/licenses/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.android.library") 7 | id("app.tivi.kotlin.multiplatform") 8 | alias(libs.plugins.kotlin.serialization) 9 | } 10 | 11 | kotlin { 12 | sourceSets { 13 | commonMain { 14 | dependencies { 15 | api(projects.core.base) 16 | implementation(libs.kotlinx.serialization) 17 | } 18 | } 19 | } 20 | } 21 | 22 | android { 23 | namespace = "app.tivi.data.licenses" 24 | } 25 | -------------------------------------------------------------------------------- /data/licenses/src/androidMain/kotlin/app/tivi/data/licenses/LicenseDataPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.licenses 5 | 6 | import app.tivi.data.licenses.fetcher.AndroidLicensesFetcherImpl 7 | import app.tivi.data.licenses.fetcher.LicensesFetcher 8 | import app.tivi.inject.ApplicationScope 9 | import me.tatarka.inject.annotations.Provides 10 | 11 | actual interface LicenseDataPlatformComponent { 12 | 13 | @ApplicationScope 14 | @Provides 15 | fun bindLicensesFetcher(fetcher: AndroidLicensesFetcherImpl): LicensesFetcher = fetcher 16 | } 17 | -------------------------------------------------------------------------------- /data/licenses/src/commonMain/kotlin/app/tivi/data/licenses/LicenseDataComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.licenses 5 | 6 | import app.tivi.data.licenses.store.LicensesStore 7 | import app.tivi.data.licenses.store.LicensesStoreImpl 8 | import app.tivi.inject.ApplicationScope 9 | import me.tatarka.inject.annotations.Provides 10 | 11 | expect interface LicenseDataPlatformComponent 12 | 13 | interface LicenseDataComponent : LicenseDataPlatformComponent { 14 | @ApplicationScope 15 | @Provides 16 | fun bindLicensesStore(bind: LicensesStoreImpl): LicensesStore = bind 17 | } 18 | -------------------------------------------------------------------------------- /data/licenses/src/commonMain/kotlin/app/tivi/data/licenses/LicensesState.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.licenses 5 | 6 | import kotlinx.serialization.Serializable 7 | 8 | @Serializable 9 | data class LicenseItem( 10 | val groupId: String, 11 | val artifactId: String, 12 | val version: String, 13 | val spdxLicenses: List?, 14 | val name: String?, 15 | val scm: Scm?, 16 | ) 17 | 18 | @Serializable 19 | data class SpdxLicense( 20 | val identifier: String, 21 | val name: String, 22 | val url: String, 23 | ) 24 | 25 | @Serializable 26 | data class Scm( 27 | val url: String, 28 | ) 29 | -------------------------------------------------------------------------------- /data/licenses/src/commonMain/kotlin/app/tivi/data/licenses/fetcher/LicensesFetcher.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.licenses.fetcher 5 | 6 | import app.tivi.data.licenses.LicenseItem 7 | 8 | interface LicensesFetcher { 9 | suspend operator fun invoke(): List 10 | } 11 | -------------------------------------------------------------------------------- /data/licenses/src/commonMain/kotlin/app/tivi/data/licenses/store/LicensesStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.licenses.store 5 | 6 | import app.tivi.data.licenses.LicenseItem 7 | 8 | interface LicensesStore { 9 | suspend fun getLicenses(): List 10 | } 11 | -------------------------------------------------------------------------------- /data/licenses/src/iosMain/kotlin/app/tivi/data/licenses/LicenseDataPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.licenses 5 | 6 | import app.tivi.data.licenses.fetcher.IosLicensesFetcherImpl 7 | import app.tivi.data.licenses.fetcher.LicensesFetcher 8 | import app.tivi.inject.ApplicationScope 9 | import me.tatarka.inject.annotations.Provides 10 | 11 | actual interface LicenseDataPlatformComponent { 12 | 13 | @ApplicationScope 14 | @Provides 15 | fun bindLicensesFetcher(fetcher: IosLicensesFetcherImpl): LicensesFetcher = fetcher 16 | } 17 | -------------------------------------------------------------------------------- /data/licenses/src/jvmMain/kotlin/app/tivi/data/licenses/LicenseDataPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.licenses 5 | 6 | import app.tivi.data.licenses.fetcher.JvmLicensesFetcherImpl 7 | import app.tivi.data.licenses.fetcher.LicensesFetcher 8 | import app.tivi.inject.ApplicationScope 9 | import me.tatarka.inject.annotations.Provides 10 | 11 | actual interface LicenseDataPlatformComponent { 12 | 13 | @ApplicationScope 14 | @Provides 15 | fun bindLicensesFetcher(fetcher: JvmLicensesFetcherImpl): LicensesFetcher = fetcher 16 | } 17 | -------------------------------------------------------------------------------- /data/licenses/src/jvmMain/kotlin/app/tivi/data/licenses/fetcher/JvmLicensesFetcherImpl.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.licenses.fetcher 5 | 6 | import app.tivi.data.licenses.LicenseItem 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | class JvmLicensesFetcherImpl : LicensesFetcher { 11 | override suspend fun invoke(): List { 12 | return emptyList() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /data/models/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.core.base) 14 | api(libs.kotlinx.datetime) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/compoundmodels/EpisodeWithSeason.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.compoundmodels 5 | 6 | import app.tivi.data.models.Episode 7 | import app.tivi.data.models.Season 8 | 9 | data class EpisodeWithSeason( 10 | val episode: Episode, 11 | val season: Season, 12 | ) 13 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/compoundmodels/LibraryShow.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.compoundmodels 5 | 6 | import app.tivi.data.models.TiviShow 7 | import app.tivi.data.models.WatchedShowEntry 8 | import app.tivi.data.views.ShowsWatchStats 9 | 10 | data class LibraryShow( 11 | val show: TiviShow, 12 | val stats: ShowsWatchStats?, 13 | val watchedEntry: WatchedShowEntry?, 14 | ) 15 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/compoundmodels/SeasonWithEpisodes.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.compoundmodels 5 | 6 | import app.tivi.data.models.Episode 7 | import app.tivi.data.models.Season 8 | 9 | data class SeasonWithEpisodes( 10 | val season: Season, 11 | val episodes: List = emptyList(), 12 | ) 13 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/compoundmodels/SeasonWithShow.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.compoundmodels 5 | 6 | import app.tivi.data.models.Season 7 | import app.tivi.data.models.TiviShow 8 | 9 | data class SeasonWithShow( 10 | val season: Season, 11 | val show: TiviShow, 12 | ) 13 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/compoundmodels/ShowSeasonEpisode.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.compoundmodels 5 | 6 | import app.tivi.data.models.Episode 7 | import app.tivi.data.models.Season 8 | import app.tivi.data.models.TiviShow 9 | 10 | data class ShowSeasonEpisode( 11 | val show: TiviShow, 12 | val season: Season, 13 | val episode: Episode, 14 | ) 15 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/compoundmodels/UpNextEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.compoundmodels 5 | 6 | import app.tivi.data.models.Episode 7 | import app.tivi.data.models.Season 8 | import app.tivi.data.models.TiviShow 9 | 10 | data class UpNextEntry( 11 | val show: TiviShow, 12 | val season: Season, 13 | val episode: Episode, 14 | ) 15 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/imagemodels/EpisodeImageModel.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.imagemodels 5 | 6 | import app.tivi.data.models.Episode 7 | 8 | data class EpisodeImageModel(val id: Long) : ImageModel 9 | 10 | fun Episode.asImageModel(): EpisodeImageModel = EpisodeImageModel(id = id) 11 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/imagemodels/ImageModel.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.imagemodels 5 | 6 | interface ImageModel 7 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/imagemodels/SeasonImageModel.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.imagemodels 5 | 6 | import app.tivi.data.models.Season 7 | 8 | data class SeasonImageModel(val id: Long) : ImageModel 9 | 10 | fun Season.asImageModel(): SeasonImageModel = SeasonImageModel(id = id) 11 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/imagemodels/ShowImageModel.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.imagemodels 5 | 6 | import app.tivi.data.models.ImageType 7 | import app.tivi.data.models.TiviShow 8 | 9 | data class ShowImageModel( 10 | val id: Long, 11 | val imageType: ImageType = ImageType.BACKDROP, 12 | ) : ImageModel 13 | 14 | fun TiviShow.asImageModel( 15 | imageType: ImageType, 16 | ): ShowImageModel = ShowImageModel(id = id, imageType = imageType) 17 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/ActionDate.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | enum class ActionDate { 7 | NOW, 8 | AIR_DATE, 9 | } 10 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/AnticipatedShowEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | data class AnticipatedShowEntry( 7 | override val id: Long = 0, 8 | override val showId: Long, 9 | override val page: Int, 10 | val pageOrder: Int, 11 | ) : PaginatedEntry 12 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/Entry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | interface Entry : TiviEntity { 7 | val showId: Long 8 | } 9 | 10 | interface MultipleEntry : Entry { 11 | val otherShowId: Long 12 | } 13 | 14 | interface PaginatedEntry : Entry { 15 | val page: Int 16 | } 17 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/EpisodeWatchEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | import kotlinx.datetime.Instant 7 | 8 | data class EpisodeWatchEntry( 9 | override val id: Long = 0, 10 | val episodeId: Long, 11 | val traktId: Long? = null, 12 | val watchedAt: Instant, 13 | val pendingAction: PendingAction = PendingAction.NOTHING, 14 | ) : TiviEntity 15 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/FollowedShowEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | import kotlinx.datetime.Instant 7 | 8 | data class FollowedShowEntry( 9 | override val id: Long = 0, 10 | override val showId: Long, 11 | val followedAt: Instant? = null, 12 | val pendingAction: PendingAction = PendingAction.NOTHING, 13 | val traktId: Long? = null, 14 | ) : Entry 15 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/PendingAction.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | enum class PendingAction(val value: String) { 7 | NOTHING("nothing"), 8 | UPLOAD("upload"), 9 | DELETE("delete"), 10 | } 11 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/PopularShowEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | data class PopularShowEntry( 7 | override val id: Long = 0, 8 | override val showId: Long, 9 | override val page: Int, 10 | val pageOrder: Int, 11 | ) : PaginatedEntry 12 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/RecommendedShowEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | data class RecommendedShowEntry( 7 | override val id: Long = 0, 8 | override val showId: Long, 9 | override val page: Int, 10 | ) : PaginatedEntry 11 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/RelatedShowEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | data class RelatedShowEntry( 7 | override val id: Long = 0, 8 | override val showId: Long, 9 | override val otherShowId: Long, 10 | val orderIndex: Int, 11 | ) : MultipleEntry 12 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/ShowStatus.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | enum class ShowStatus(val storageKey: String) { 7 | ENDED("ended"), 8 | RETURNING("returning"), 9 | CANCELED("canceled"), 10 | IN_PRODUCTION("inproduction"), 11 | PLANNED("planned"), 12 | } 13 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/ShowTmdbImage.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | data class ShowTmdbImage( 7 | override val id: Long = 0, 8 | val showId: Long, 9 | override val path: String, 10 | override val type: ImageType, 11 | override val language: String? = null, 12 | override val rating: Float = 0f, 13 | override val isPrimary: Boolean = false, 14 | ) : TiviEntity, 15 | TmdbImageEntity 16 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/SortOption.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | enum class SortOption(val sqlValue: String) { 7 | LAST_WATCHED("last_watched"), 8 | AIR_DATE("recently_aired"), 9 | ALPHABETICAL("alpha"), 10 | DATE_ADDED("added"), 11 | } 12 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/TiviEntity.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | interface TiviEntity { 7 | val id: Long 8 | } 9 | 10 | interface TraktIdEntity { 11 | val traktId: Int? 12 | } 13 | 14 | interface TmdbIdEntity { 15 | val tmdbId: Int? 16 | } 17 | 18 | interface TmdbImageEntity : TiviEntity { 19 | val path: String 20 | val type: ImageType 21 | val language: String? 22 | val rating: Float 23 | val isPrimary: Boolean 24 | } 25 | 26 | enum class ImageType(val storageKey: String) { 27 | BACKDROP("backdrop"), 28 | POSTER("poster"), 29 | LOGO("logo"), 30 | } 31 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/TraktUser.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | import kotlinx.datetime.Instant 7 | 8 | data class TraktUser( 9 | override val id: Long = 0, 10 | val username: String, 11 | val name: String? = null, 12 | val joined: Instant? = null, 13 | val location: String? = null, 14 | val about: String? = null, 15 | val avatarUrl: String? = null, 16 | val vip: Boolean? = null, 17 | val isMe: Boolean = false, 18 | ) : TiviEntity 19 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/TrendingShowEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | data class TrendingShowEntry( 7 | override val id: Long = 0, 8 | override val showId: Long, 9 | override val page: Int, 10 | val watchers: Int, 11 | ) : PaginatedEntry 12 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/models/WatchedShowEntry.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.models 5 | 6 | import kotlinx.datetime.Instant 7 | 8 | data class WatchedShowEntry( 9 | override val id: Long = 0, 10 | override val showId: Long, 11 | val lastWatched: Instant, 12 | val lastUpdated: Instant, 13 | ) : Entry 14 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/util/DateTimeUtils.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.util 5 | 6 | import kotlin.time.Duration 7 | import kotlinx.datetime.Clock 8 | import kotlinx.datetime.Instant 9 | 10 | inline val Duration.inPast: Instant 11 | get() = Clock.System.now() - this 12 | 13 | fun Instant.durationSinceNow(): Duration = Clock.System.now() - this 14 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/util/ImageEntityUtils.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.util 5 | 6 | import app.tivi.data.models.ImageType 7 | import app.tivi.data.models.TmdbImageEntity 8 | 9 | internal fun findHighestRatedItem(items: Collection, type: ImageType): T? { 10 | if (items.size <= 1) { 11 | return items.firstOrNull() 12 | } 13 | return items.asSequence() 14 | .filter { it.type == type } 15 | .maxByOrNull { it.rating + (if (it.isPrimary) 10f else 0f) } 16 | } 17 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/views/ShowsNextToWatch.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.views 5 | 6 | data class ShowsNextToWatch( 7 | val showId: Long, 8 | val seasonId: Long, 9 | val episodeId: Long, 10 | ) 11 | -------------------------------------------------------------------------------- /data/models/src/commonMain/kotlin/app/tivi/data/views/ShowsWatchStats.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.views 5 | 6 | data class ShowsWatchStats( 7 | val showId: Long, 8 | val episodeCount: Int, 9 | val watchedEpisodeCount: Int, 10 | ) 11 | 12 | /** 13 | * Only exists to make it easier to map from SqlDelight 14 | */ 15 | fun ShowsWatchStats( 16 | showId: Long, 17 | episodeCount: Long, 18 | watchedEpisodeCount: Long, 19 | ): ShowsWatchStats = ShowsWatchStats(showId, episodeCount.toInt(), watchedEpisodeCount.toInt()) 20 | -------------------------------------------------------------------------------- /data/popularshows/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.data.models) 14 | implementation(projects.data.db) 15 | implementation(projects.data.legacy) // remove this eventually 16 | 17 | implementation(projects.api.trakt) 18 | implementation(projects.api.tmdb) 19 | 20 | api(libs.store) 21 | implementation(libs.kotlinx.atomicfu) 22 | 23 | implementation(libs.kotlininject.runtime) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /data/popularshows/src/commonMain/kotlin/app/tivi/data/popularshows/PopularShowsBinds.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.popularshows 5 | 6 | import me.tatarka.inject.annotations.Provides 7 | 8 | interface PopularShowsBinds { 9 | @Provides 10 | fun provideTraktPopularShowsDataSource( 11 | bind: TraktPopularShowsDataSource, 12 | ): PopularShowsDataSource = bind 13 | } 14 | -------------------------------------------------------------------------------- /data/popularshows/src/commonMain/kotlin/app/tivi/data/popularshows/PopularShowsDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.popularshows 5 | 6 | import app.tivi.data.models.PopularShowEntry 7 | import app.tivi.data.models.TiviShow 8 | 9 | fun interface PopularShowsDataSource { 10 | suspend operator fun invoke( 11 | page: Int, 12 | pageSize: Int, 13 | ): List> 14 | } 15 | -------------------------------------------------------------------------------- /data/popularshows/src/commonMain/kotlin/app/tivi/data/popularshows/PopularShowsLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.popularshows 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.GroupLastRequestStore 8 | import app.tivi.data.models.Request 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class PopularShowsLastRequestStore( 13 | dao: LastRequestDao, 14 | ) : GroupLastRequestStore(Request.POPULAR_SHOWS, dao) 15 | -------------------------------------------------------------------------------- /data/recommendedshows/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.data.models) 14 | implementation(projects.data.db) 15 | implementation(projects.data.legacy) // remove this eventually 16 | 17 | implementation(projects.api.trakt) 18 | implementation(projects.api.tmdb) 19 | 20 | api(libs.store) 21 | implementation(libs.kotlinx.atomicfu) 22 | 23 | implementation(libs.kotlininject.runtime) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /data/recommendedshows/src/commonMain/kotlin/app/tivi/data/recommendedshows/RecommendedShowsBinds.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.recommendedshows 5 | 6 | import me.tatarka.inject.annotations.Provides 7 | 8 | interface RecommendedShowsBinds { 9 | @Provides 10 | fun provideTraktRecommendedShowsDataSource( 11 | bind: TraktRecommendedShowsDataSource, 12 | ): RecommendedShowsDataSource = bind 13 | } 14 | -------------------------------------------------------------------------------- /data/recommendedshows/src/commonMain/kotlin/app/tivi/data/recommendedshows/RecommendedShowsDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.recommendedshows 5 | 6 | import app.tivi.data.models.TiviShow 7 | 8 | interface RecommendedShowsDataSource { 9 | suspend operator fun invoke(page: Int, pageSize: Int): List 10 | } 11 | -------------------------------------------------------------------------------- /data/recommendedshows/src/commonMain/kotlin/app/tivi/data/recommendedshows/RecommendedShowsLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.recommendedshows 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.GroupLastRequestStore 8 | import app.tivi.data.models.Request 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class RecommendedShowsLastRequestStore( 13 | dao: LastRequestDao, 14 | ) : GroupLastRequestStore(Request.RECOMMENDED_SHOWS, dao) 15 | -------------------------------------------------------------------------------- /data/relatedshows/src/commonMain/kotlin/app/tivi/data/relatedshows/RelatedShowsDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.relatedshows 5 | 6 | import app.tivi.data.models.RelatedShowEntry 7 | import app.tivi.data.models.TiviShow 8 | 9 | fun interface RelatedShowsDataSource { 10 | suspend operator fun invoke( 11 | showId: Long, 12 | ): List> 13 | } 14 | -------------------------------------------------------------------------------- /data/relatedshows/src/commonMain/kotlin/app/tivi/data/relatedshows/RelatedShowsLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.relatedshows 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.EntityLastRequestStore 8 | import app.tivi.data.models.Request 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class RelatedShowsLastRequestStore( 13 | dao: LastRequestDao, 14 | ) : EntityLastRequestStore(Request.RELATED_SHOWS, dao) 15 | -------------------------------------------------------------------------------- /data/search/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.data.models) 14 | implementation(projects.data.db) 15 | implementation(projects.data.legacy) // remove this eventually 16 | 17 | implementation(projects.api.trakt) 18 | implementation(projects.api.tmdb) 19 | 20 | api(libs.store) 21 | implementation(libs.kotlinx.atomicfu) 22 | 23 | implementation(libs.kotlininject.runtime) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /data/search/src/commonMain/kotlin/app/tivi/data/search/SearchBinds.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.search 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | interface SearchBinds { 10 | val TmdbSearchDataSource.bind: SearchDataSource 11 | @ApplicationScope @Provides 12 | get() = this 13 | } 14 | -------------------------------------------------------------------------------- /data/search/src/commonMain/kotlin/app/tivi/data/search/SearchDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.search 5 | 6 | import app.tivi.data.models.ShowTmdbImage 7 | import app.tivi.data.models.TiviShow 8 | 9 | interface SearchDataSource { 10 | suspend fun search(query: String): List>> 11 | } 12 | -------------------------------------------------------------------------------- /data/showimages/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.data.models) 14 | implementation(projects.data.db) 15 | implementation(projects.data.legacy) // remove this eventually 16 | 17 | implementation(projects.api.trakt) 18 | implementation(projects.api.tmdb) 19 | 20 | api(libs.store) 21 | implementation(libs.kotlinx.atomicfu) 22 | 23 | implementation(libs.kotlininject.runtime) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /data/showimages/src/commonMain/kotlin/app/tivi/data/showimages/ShowImagesBinds.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.showimages 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | interface ShowImagesBinds { 10 | @ApplicationScope 11 | @Provides 12 | fun bindShowImagesDataSource(bind: TmdbShowImagesDataSource): ShowImagesDataSource = bind 13 | } 14 | -------------------------------------------------------------------------------- /data/showimages/src/commonMain/kotlin/app/tivi/data/showimages/ShowImagesDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.showimages 5 | 6 | import app.tivi.data.models.ShowTmdbImage 7 | import app.tivi.data.models.TiviShow 8 | 9 | interface ShowImagesDataSource { 10 | suspend fun getShowImages(show: TiviShow): List 11 | } 12 | -------------------------------------------------------------------------------- /data/showimages/src/commonMain/kotlin/app/tivi/data/showimages/ShowImagesLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.showimages 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.EntityLastRequestStore 8 | import app.tivi.data.models.Request 9 | import app.tivi.inject.ApplicationScope 10 | import me.tatarka.inject.annotations.Inject 11 | 12 | @ApplicationScope 13 | @Inject 14 | class ShowImagesLastRequestStore( 15 | dao: LastRequestDao, 16 | ) : EntityLastRequestStore(Request.SHOW_IMAGES, dao) 17 | -------------------------------------------------------------------------------- /data/shows/src/commonMain/kotlin/app/tivi/data/shows/ShowDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.shows 5 | 6 | import app.tivi.data.models.TiviShow 7 | 8 | interface ShowDataSource { 9 | suspend fun getShow(show: TiviShow): TiviShow 10 | } 11 | -------------------------------------------------------------------------------- /data/shows/src/commonMain/kotlin/app/tivi/data/shows/ShowLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.shows 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.EntityLastRequestStore 8 | import app.tivi.data.models.Request 9 | import app.tivi.inject.ApplicationScope 10 | import me.tatarka.inject.annotations.Inject 11 | 12 | @ApplicationScope 13 | @Inject 14 | class ShowLastRequestStore( 15 | dao: LastRequestDao, 16 | ) : EntityLastRequestStore(Request.SHOW_DETAILS, dao) 17 | -------------------------------------------------------------------------------- /data/shows/src/commonMain/kotlin/app/tivi/data/shows/ShowsBinds.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.shows 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | interface ShowsBinds { 10 | @ApplicationScope 11 | @Provides 12 | fun bindTraktShowDataSource(bind: TraktShowDataSourceImpl): TraktShowDataSource = bind 13 | 14 | @ApplicationScope 15 | @Provides 16 | fun bindTmdbShowDataSource(bind: TmdbShowDataSourceImpl): TmdbShowDataSource = bind 17 | } 18 | 19 | typealias TmdbShowDataSource = ShowDataSource 20 | typealias TraktShowDataSource = ShowDataSource 21 | -------------------------------------------------------------------------------- /data/test/src/androidUnitTest/kotlin/app/tivi/data/DatabaseTest.kt: -------------------------------------------------------------------------------- 1 | package app.tivi.data 2 | 3 | import app.cash.sqldelight.db.SqlDriver 4 | import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver 5 | 6 | internal actual fun createTestSqlDriver(name: String): SqlDriver { 7 | return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY).also { db -> 8 | Database.Schema.create(db) 9 | db.execute(null, "PRAGMA foreign_keys=ON", 0) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /data/test/src/commonTest/kotlin/app/tivi/utils/Dispatchers.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.utils 5 | 6 | import app.tivi.util.AppCoroutineDispatchers 7 | import kotlinx.coroutines.CoroutineDispatcher 8 | import kotlinx.coroutines.test.StandardTestDispatcher 9 | 10 | fun createSingleAppCoroutineDispatchers( 11 | testDispatcher: CoroutineDispatcher = StandardTestDispatcher(), 12 | ): AppCoroutineDispatchers = AppCoroutineDispatchers( 13 | io = testDispatcher, 14 | databaseRead = testDispatcher, 15 | databaseWrite = testDispatcher, 16 | computation = testDispatcher, 17 | main = testDispatcher, 18 | ) 19 | -------------------------------------------------------------------------------- /data/test/src/commonTest/kotlin/app/tivi/utils/FakeEpisodeDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.utils 5 | 6 | import app.tivi.data.episodes.TraktEpisodeDataSource 7 | import app.tivi.data.models.Episode 8 | 9 | class FakeEpisodeDataSource : TraktEpisodeDataSource { 10 | var result = Result.success(Episode.EMPTY) 11 | 12 | override suspend fun getEpisode(showId: Long, seasonNumber: Int, episodeNumber: Int): Episode { 13 | return result.getOrThrow() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /data/test/src/commonTest/kotlin/app/tivi/utils/Fakes.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.utils 5 | 6 | import app.tivi.data.traktauth.AuthState 7 | import app.tivi.data.traktauth.TraktLoginAction 8 | import app.tivi.data.traktauth.TraktRefreshTokenAction 9 | 10 | object SuccessTraktLoginAction : TraktLoginAction { 11 | override suspend fun invoke(): AuthState = AuthorizedAuthState 12 | } 13 | 14 | object SuccessRefreshTokenAction : TraktRefreshTokenAction { 15 | override suspend fun invoke(state: AuthState): AuthState = AuthorizedAuthState 16 | } 17 | -------------------------------------------------------------------------------- /data/test/src/commonTest/kotlin/app/tivi/utils/TestTransactionRunner.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.utils 5 | 6 | import app.tivi.data.db.DatabaseTransactionRunner 7 | 8 | internal object TestTransactionRunner : DatabaseTransactionRunner { 9 | override fun invoke(block: () -> T): T = block() 10 | } 11 | -------------------------------------------------------------------------------- /data/test/src/jvmTest/kotlin/app/tivi/data/DatabaseTest.kt: -------------------------------------------------------------------------------- 1 | package app.tivi.data 2 | 3 | import app.cash.sqldelight.db.SqlDriver 4 | import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver 5 | 6 | internal actual fun createTestSqlDriver(name: String): SqlDriver { 7 | return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY).also { db -> 8 | Database.Schema.create(db) 9 | db.execute(null, "PRAGMA foreign_keys=ON", 0) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /data/test/src/nativeTest/kotlin/app/tivi/data/DatabaseTest.kt: -------------------------------------------------------------------------------- 1 | package app.tivi.data 2 | 3 | import app.cash.sqldelight.db.SqlDriver 4 | import app.cash.sqldelight.driver.native.inMemoryDriver 5 | 6 | internal actual fun createTestSqlDriver(name: String): SqlDriver { 7 | return inMemoryDriver(Database.Schema, name).also { driver -> 8 | driver.execute(null, "PRAGMA foreign_keys=ON", 0) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /data/traktauth/src/androidMain/kotlin/app/tivi/data/traktauth/TraktAuthInitializer.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktauth 5 | 6 | import app.tivi.appinitializers.AppInitializer 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | class TraktAuthInitializer( 11 | private val traktLoginAction: Lazy, 12 | ) : AppInitializer { 13 | override fun initialize() { 14 | traktLoginAction.value.registerActivityWatcher() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /data/traktauth/src/commonMain/kotlin/app/tivi/data/traktauth/TraktAuthComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktauth 5 | 6 | expect interface TraktAuthComponent 7 | -------------------------------------------------------------------------------- /data/traktauth/src/commonMain/kotlin/app/tivi/data/traktauth/TraktAuthState.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktauth 5 | 6 | enum class TraktAuthState { 7 | LOGGED_IN, 8 | LOGGED_OUT, 9 | } 10 | -------------------------------------------------------------------------------- /data/traktauth/src/commonMain/kotlin/app/tivi/data/traktauth/TraktLoginAction.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktauth 5 | 6 | interface TraktLoginAction { 7 | suspend operator fun invoke(): AuthState? 8 | } 9 | -------------------------------------------------------------------------------- /data/traktauth/src/commonMain/kotlin/app/tivi/data/traktauth/TraktOAuthInfo.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktauth 5 | 6 | data class TraktOAuthInfo( 7 | val clientId: String, 8 | val clientSecret: String, 9 | val redirectUri: String, 10 | ) 11 | -------------------------------------------------------------------------------- /data/traktauth/src/commonMain/kotlin/app/tivi/data/traktauth/TraktRefreshTokenAction.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktauth 5 | 6 | interface TraktRefreshTokenAction { 7 | suspend operator fun invoke(state: AuthState): AuthState? 8 | } 9 | -------------------------------------------------------------------------------- /data/traktauth/src/commonMain/kotlin/app/tivi/data/traktauth/store/AuthStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktauth.store 5 | 6 | import app.tivi.data.traktauth.AuthState 7 | 8 | interface AuthStore { 9 | suspend fun get(): AuthState? 10 | suspend fun save(state: AuthState) 11 | suspend fun clear() 12 | suspend fun isAvailable(): Boolean = true 13 | } 14 | -------------------------------------------------------------------------------- /data/traktauth/src/jvmMain/kotlin/app/tivi/data/traktauth/DesktopAuthStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktauth 5 | 6 | import app.tivi.data.traktauth.store.AuthStore 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | class DesktopAuthStore : AuthStore { 11 | override suspend fun get(): AuthState? { 12 | // TODO no-op for now 13 | return null 14 | } 15 | 16 | override suspend fun save(state: AuthState) { 17 | // TODO no-op for now 18 | } 19 | 20 | override suspend fun clear() { 21 | // TODO no-op for now 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /data/traktauth/src/jvmMain/kotlin/app/tivi/data/traktauth/DesktopTraktLoginAction.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktauth 5 | 6 | import me.tatarka.inject.annotations.Inject 7 | 8 | @Inject 9 | class DesktopTraktLoginAction : TraktLoginAction { 10 | override suspend operator fun invoke(): AuthState? { 11 | return null 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /data/traktauth/src/jvmMain/kotlin/app/tivi/data/traktauth/DesktopTraktRefreshTokenAction.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktauth 5 | 6 | import me.tatarka.inject.annotations.Inject 7 | 8 | @Inject 9 | class DesktopTraktRefreshTokenAction : TraktRefreshTokenAction { 10 | override suspend fun invoke(state: AuthState): AuthState? { 11 | // TODO 12 | return null 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /data/traktusers/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.data.models) 14 | implementation(projects.data.db) 15 | implementation(projects.data.legacy) // remove this eventually 16 | 17 | implementation(projects.api.trakt) 18 | implementation(projects.api.tmdb) 19 | 20 | api(libs.store) 21 | implementation(libs.kotlinx.atomicfu) 22 | 23 | implementation(libs.kotlininject.runtime) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /data/traktusers/src/commonMain/kotlin/app/tivi/data/traktusers/TraktUsersBinds.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktusers 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | interface TraktUsersBinds { 10 | @ApplicationScope 11 | @Provides 12 | fun provideTraktUsersDataSource(bind: TraktUsersDataSource): UsersDataSource = bind 13 | } 14 | -------------------------------------------------------------------------------- /data/traktusers/src/commonMain/kotlin/app/tivi/data/traktusers/TraktUsersLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktusers 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.EntityLastRequestStore 8 | import app.tivi.data.models.Request 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class TraktUsersLastRequestStore( 13 | dao: LastRequestDao, 14 | ) : EntityLastRequestStore(Request.USER_PROFILE, dao) 15 | -------------------------------------------------------------------------------- /data/traktusers/src/commonMain/kotlin/app/tivi/data/traktusers/UsersDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.traktusers 5 | 6 | import app.tivi.data.models.TraktUser 7 | 8 | fun interface UsersDataSource { 9 | suspend fun getUser(slug: String): TraktUser 10 | } 11 | -------------------------------------------------------------------------------- /data/trendingshows/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.data.models) 14 | implementation(projects.data.db) 15 | implementation(projects.data.legacy) // remove this eventually 16 | 17 | implementation(projects.api.trakt) 18 | implementation(projects.api.tmdb) 19 | 20 | api(libs.store) 21 | implementation(libs.kotlinx.atomicfu) 22 | 23 | implementation(libs.kotlininject.runtime) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /data/trendingshows/src/commonMain/kotlin/app/tivi/data/trendingshows/TrendingShowsBinds.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.trendingshows 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | interface TrendingShowsBinds { 10 | @Provides 11 | @ApplicationScope 12 | fun provideTraktTrendingShowsDataSource( 13 | bind: TraktTrendingShowsDataSource, 14 | ): TrendingShowsDataSource = bind 15 | } 16 | -------------------------------------------------------------------------------- /data/trendingshows/src/commonMain/kotlin/app/tivi/data/trendingshows/TrendingShowsDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.trendingshows 5 | 6 | import app.tivi.data.models.TiviShow 7 | import app.tivi.data.models.TrendingShowEntry 8 | 9 | fun interface TrendingShowsDataSource { 10 | suspend operator fun invoke( 11 | page: Int, 12 | pageSize: Int, 13 | ): List> 14 | } 15 | -------------------------------------------------------------------------------- /data/trendingshows/src/commonMain/kotlin/app/tivi/data/trendingshows/TrendingShowsLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.trendingshows 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.GroupLastRequestStore 8 | import app.tivi.data.models.Request 9 | import app.tivi.inject.ApplicationScope 10 | import me.tatarka.inject.annotations.Inject 11 | 12 | @ApplicationScope 13 | @Inject 14 | class TrendingShowsLastRequestStore( 15 | dao: LastRequestDao, 16 | ) : GroupLastRequestStore(Request.TRENDING_SHOWS, dao) 17 | -------------------------------------------------------------------------------- /data/watchedshows/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.kotlin.multiplatform") 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | commonMain { 12 | dependencies { 13 | api(projects.data.models) 14 | implementation(projects.data.db) 15 | implementation(projects.data.legacy) // remove this eventually 16 | 17 | implementation(projects.api.trakt) 18 | implementation(projects.api.tmdb) 19 | 20 | api(libs.store) 21 | implementation(libs.kotlinx.atomicfu) 22 | 23 | implementation(libs.kotlininject.runtime) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /data/watchedshows/src/commonMain/kotlin/app/tivi/data/watchedshows/WatchedShowsBinds.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.watchedshows 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | interface WatchedShowsBinds { 10 | @Provides 11 | @ApplicationScope 12 | fun provideTraktWatchedShowsDataSource( 13 | bind: TraktWatchedShowsDataSource, 14 | ): WatchedShowsDataSource = bind 15 | } 16 | -------------------------------------------------------------------------------- /data/watchedshows/src/commonMain/kotlin/app/tivi/data/watchedshows/WatchedShowsDataSource.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.watchedshows 5 | 6 | import app.tivi.data.models.TiviShow 7 | import app.tivi.data.models.WatchedShowEntry 8 | 9 | fun interface WatchedShowsDataSource { 10 | suspend operator fun invoke(): List> 11 | } 12 | -------------------------------------------------------------------------------- /data/watchedshows/src/commonMain/kotlin/app/tivi/data/watchedshows/WatchedShowsLastRequestStore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.data.watchedshows 5 | 6 | import app.tivi.data.daos.LastRequestDao 7 | import app.tivi.data.lastrequests.GroupLastRequestStore 8 | import app.tivi.data.models.Request 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class WatchedShowsLastRequestStore( 13 | dao: LastRequestDao, 14 | ) : GroupLastRequestStore(Request.WATCHED_SHOWS, dao) 15 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /domain/src/commonMain/kotlin/app/tivi/domain/interactors/FetchLicensesList.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.domain.interactors 5 | 6 | import app.tivi.data.licenses.LicenseItem 7 | import app.tivi.data.licenses.store.LicensesStore 8 | import app.tivi.domain.Interactor 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class FetchLicensesList( 13 | licensesStore: Lazy, 14 | ) : Interactor>() { 15 | private val licensesStore by licensesStore 16 | 17 | override suspend fun doWork(params: Unit): List { 18 | return licensesStore.getLicenses() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /domain/src/commonMain/kotlin/app/tivi/domain/interactors/LoginTrakt.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.domain.interactors 5 | 6 | import app.tivi.data.traktauth.AuthState 7 | import app.tivi.data.traktauth.TraktAuthRepository 8 | import app.tivi.domain.Interactor 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class LoginTrakt( 13 | traktAuthRepository: Lazy, 14 | ) : Interactor() { 15 | private val traktAuthRepository by traktAuthRepository 16 | 17 | override suspend fun doWork(params: Unit) = traktAuthRepository.login() 18 | } 19 | -------------------------------------------------------------------------------- /domain/src/commonMain/kotlin/app/tivi/domain/interactors/LogoutTrakt.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.domain.interactors 5 | 6 | import app.tivi.data.traktauth.TraktAuthRepository 7 | import app.tivi.domain.Interactor 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | class LogoutTrakt( 12 | private val traktAuthRepository: Lazy, 13 | private val clearUserDetails: Lazy, 14 | ) : Interactor() { 15 | override suspend fun doWork(params: Unit) { 16 | traktAuthRepository.value.logout() 17 | clearUserDetails.value.invoke(ClearUserDetails.Params("me")) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /domain/src/commonMain/kotlin/app/tivi/domain/interactors/RefreshTraktTokens.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.domain.interactors 5 | 6 | import app.tivi.data.traktauth.AuthState 7 | import app.tivi.data.traktauth.TraktAuthRepository 8 | import app.tivi.domain.Interactor 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | class RefreshTraktTokens( 13 | traktAuthRepository: Lazy, 14 | ) : Interactor() { 15 | private val traktAuthRepository by traktAuthRepository 16 | override suspend fun doWork(params: Unit) = traktAuthRepository.refreshTokens() 17 | } 18 | -------------------------------------------------------------------------------- /fastlane/Matchfile: -------------------------------------------------------------------------------- 1 | git_url("https://github.com/chrisbanes/ios-certificates.git") 2 | storage_mode("git") 3 | type("appstore") 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en_US/full_description.txt: -------------------------------------------------------------------------------- 1 | Discover and track your favorite TV shows with Tivi, the ultimate app for Trakt.tv users. Seamlessly sync your watchlist and browse trending titles. With Tivi, you can manage your viewing history, rate and review content, and receive personalized recommendations. 2 | 3 | You may be wondering why the screenshots are filled with images of animals? Well rest assured, the app will display show imagery as expected. We just can't include them in the screenshots. 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en_US/images/featureGraphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/metadata/android/en_US/images/featureGraphic.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en_US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/metadata/android/en_US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en_US/images/phoneScreenshots/0_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/metadata/android/en_US/images/phoneScreenshots/0_home.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en_US/images/phoneScreenshots/1_show_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/metadata/android/en_US/images/phoneScreenshots/1_show_details.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en_US/images/phoneScreenshots/2_upnext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/metadata/android/en_US/images/phoneScreenshots/2_upnext.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en_US/images/phoneScreenshots/3_library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/metadata/android/en_US/images/phoneScreenshots/3_library.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en_US/images/phoneScreenshots/4_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/metadata/android/en_US/images/phoneScreenshots/4_search.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en_US/short_description.txt: -------------------------------------------------------------------------------- 1 | Your ultimate Trakt.tv companion to discover and track TV shows 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en_US/title.txt: -------------------------------------------------------------------------------- 1 | Tivi -------------------------------------------------------------------------------- /fastlane/metadata/android/en_US/video.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/metadata/android/en_US/video.txt -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPad Pro (12.9-inch) (3rd generation)-1_Home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPad Pro (12.9-inch) (3rd generation)-1_Home.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPad Pro (12.9-inch) (3rd generation)-2_ShowDetails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPad Pro (12.9-inch) (3rd generation)-2_ShowDetails.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPad Pro (12.9-inch) (3rd generation)-3_UpNext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPad Pro (12.9-inch) (3rd generation)-3_UpNext.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPad Pro (12.9-inch) (3rd generation)-4_Library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPad Pro (12.9-inch) (3rd generation)-4_Library.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPad Pro (12.9-inch) (3rd generation)-5_Search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPad Pro (12.9-inch) (3rd generation)-5_Search.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPhone 15 Pro Max-1_Home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPhone 15 Pro Max-1_Home.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPhone 15 Pro Max-2_ShowDetails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPhone 15 Pro Max-2_ShowDetails.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPhone 15 Pro Max-3_UpNext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPhone 15 Pro Max-3_UpNext.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPhone 15 Pro Max-4_Library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPhone 15 Pro Max-4_Library.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPhone 15 Pro Max-5_Search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPhone 15 Pro Max-5_Search.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPhone SE (3rd generation)-1_Home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPhone SE (3rd generation)-1_Home.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPhone SE (3rd generation)-2_ShowDetails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPhone SE (3rd generation)-2_ShowDetails.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPhone SE (3rd generation)-3_UpNext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPhone SE (3rd generation)-3_UpNext.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPhone SE (3rd generation)-4_Library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPhone SE (3rd generation)-4_Library.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iPhone SE (3rd generation)-5_Search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/fastlane/screenshots/en-US/iPhone SE (3rd generation)-5_Search.png -------------------------------------------------------------------------------- /gradle/build-logic/convention/src/main/kotlin/app/tivi/gradle/AndroidApplicationConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.gradle 5 | 6 | import org.gradle.api.Plugin 7 | import org.gradle.api.Project 8 | 9 | class AndroidApplicationConventionPlugin : Plugin { 10 | override fun apply(target: Project) { 11 | with(target) { 12 | with(pluginManager) { 13 | apply("com.android.application") 14 | apply("org.gradle.android.cache-fix") 15 | } 16 | 17 | configureAndroid() 18 | configureLicensee() 19 | configureAndroidLicensesTasks() 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /gradle/build-logic/convention/src/main/kotlin/app/tivi/gradle/AndroidLibraryConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.gradle 5 | 6 | import org.gradle.api.Plugin 7 | import org.gradle.api.Project 8 | 9 | class AndroidLibraryConventionPlugin : Plugin { 10 | override fun apply(target: Project) { 11 | with(target) { 12 | with(pluginManager) { 13 | apply("com.android.library") 14 | apply("org.gradle.android.cache-fix") 15 | } 16 | 17 | configureAndroid() 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gradle/build-logic/convention/src/main/kotlin/app/tivi/gradle/AndroidTestConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.gradle 5 | 6 | import org.gradle.api.Plugin 7 | import org.gradle.api.Project 8 | 9 | class AndroidTestConventionPlugin : Plugin { 10 | override fun apply(target: Project) { 11 | with(target) { 12 | with(pluginManager) { 13 | apply("com.android.test") 14 | apply("org.gradle.android.cache-fix") 15 | } 16 | 17 | configureAndroid() 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gradle/build-logic/convention/src/main/kotlin/app/tivi/gradle/Java.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.gradle 5 | 6 | import org.gradle.api.Project 7 | import org.gradle.api.plugins.JavaPluginExtension 8 | import org.gradle.jvm.toolchain.JavaLanguageVersion 9 | import org.gradle.kotlin.dsl.configure 10 | 11 | fun Project.configureJava() { 12 | java { 13 | toolchain { 14 | languageVersion.set(JavaLanguageVersion.of(17)) 15 | } 16 | } 17 | } 18 | 19 | private fun Project.java(action: JavaPluginExtension.() -> Unit) = extensions.configure(action) 20 | -------------------------------------------------------------------------------- /gradle/build-logic/convention/src/main/kotlin/app/tivi/gradle/Kotlin.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.gradle 5 | 6 | import org.gradle.api.Project 7 | 8 | fun Project.configureKotlin() { 9 | // Configure Java to use our chosen language level. Kotlin will automatically pick this up 10 | configureJava() 11 | } 12 | -------------------------------------------------------------------------------- /gradle/build-logic/convention/src/main/kotlin/app/tivi/gradle/KotlinAndroidConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.gradle 5 | 6 | import org.gradle.api.Plugin 7 | import org.gradle.api.Project 8 | 9 | class KotlinAndroidConventionPlugin : Plugin { 10 | override fun apply(target: Project) { 11 | with(target) { 12 | with(pluginManager) { 13 | apply("org.jetbrains.kotlin.android") 14 | } 15 | 16 | configureSpotless() 17 | configureKotlin() 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gradle/build-logic/convention/src/main/kotlin/app/tivi/gradle/RootConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.gradle 5 | 6 | import org.gradle.api.Plugin 7 | import org.gradle.api.Project 8 | 9 | class RootConventionPlugin : Plugin { 10 | override fun apply(target: Project) = with(target) { 11 | configureSpotless() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /gradle/build-logic/convention/src/main/kotlin/app/tivi/gradle/VersionCatalog.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.gradle 5 | 6 | import org.gradle.api.Project 7 | import org.gradle.api.artifacts.VersionCatalog 8 | import org.gradle.api.artifacts.VersionCatalogsExtension 9 | import org.gradle.kotlin.dsl.getByType 10 | 11 | internal val Project.libs: VersionCatalog 12 | get() = extensions.getByType().named("libs") 13 | -------------------------------------------------------------------------------- /gradle/build-logic/convention/src/main/kotlin/app/tivi/gradle/Versions.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.gradle 5 | 6 | object Versions { 7 | const val COMPILE_SDK = 34 8 | const val MIN_SDK = 24 9 | const val TARGET_SDK = 34 10 | } 11 | -------------------------------------------------------------------------------- /gradle/build-logic/gradle.properties: -------------------------------------------------------------------------------- 1 | # Gradle properties are not passed to included builds https://github.com/gradle/gradle/issues/2534 2 | org.gradle.parallel=true 3 | org.gradle.caching=true 4 | org.gradle.configureondemand=true 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /ios-app/Tivi/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '15.2' 2 | 3 | target 'Tivi' do 4 | use_frameworks! 5 | 6 | pod 'AppAuth', '1.7.5' 7 | pod 'FirebaseAnalytics', '11.4.0' 8 | pod 'FirebaseCrashlytics', '11.4.0' 9 | end 10 | -------------------------------------------------------------------------------- /ios-app/Tivi/Settings.bundle/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | DefaultValue 9 | 1.0 (1000) 10 | Key 11 | SNAppVersion 12 | Title 13 | Version 14 | Type 15 | PSTitleValueSpecifier 16 | 17 | 18 | StringsTable 19 | Root 20 | 21 | 22 | -------------------------------------------------------------------------------- /ios-app/Tivi/Tivi.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios-app/Tivi/Tivi.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios-app/Tivi/Tivi.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios-app/Tivi/Tivi.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios-app/Tivi/Tivi/Assets.xcassets/AppIcon-QA.appiconset/AppIcon-QA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/ios-app/Tivi/Tivi/Assets.xcassets/AppIcon-QA.appiconset/AppIcon-QA.png -------------------------------------------------------------------------------- /ios-app/Tivi/Tivi/Assets.xcassets/AppIcon-QA.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "AppIcon-QA.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | } 9 | ], 10 | "info" : { 11 | "author" : "xcode", 12 | "version" : 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ios-app/Tivi/Tivi/Assets.xcassets/AppIcon.appiconset/AppIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/ios-app/Tivi/Tivi/Assets.xcassets/AppIcon.appiconset/AppIcon.png -------------------------------------------------------------------------------- /ios-app/Tivi/Tivi/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "AppIcon.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | } 9 | ], 10 | "info" : { 11 | "author" : "xcode", 12 | "version" : 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ios-app/Tivi/Tivi/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ios-app/Tivi/Tivi/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ios-app/Tivi/xcconfig/Tivi-Prod-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "common/Prod.xcconfig" 2 | #include "common/Debug.xcconfig" 3 | #include "Pods/Target Support Files/Pods-Tivi/Pods-Tivi.prod debug.xcconfig" 4 | 5 | PRODUCT_NAME = $(TARGET_NAME) Dev 6 | -------------------------------------------------------------------------------- /ios-app/Tivi/xcconfig/Tivi-Prod-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "common/Prod.xcconfig" 2 | #include "common/Release.xcconfig" 3 | #include "Pods/Target Support Files/Pods-Tivi/Pods-Tivi.prod release.xcconfig" 4 | 5 | PRODUCT_NAME = $(TARGET_NAME) 6 | -------------------------------------------------------------------------------- /ios-app/Tivi/xcconfig/Tivi-QA-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "common/QA.xcconfig" 2 | #include "common/Debug.xcconfig" 3 | #include "Pods/Target Support Files/Pods-Tivi/Pods-Tivi.qa debug.xcconfig" 4 | 5 | PRODUCT_NAME = $(TARGET_NAME) QA Dev 6 | -------------------------------------------------------------------------------- /ios-app/Tivi/xcconfig/Tivi-QA-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "common/QA.xcconfig" 2 | #include "common/Release.xcconfig" 3 | #include "Pods/Target Support Files/Pods-Tivi/Pods-Tivi.qa release.xcconfig" 4 | 5 | PRODUCT_NAME = $(TARGET_NAME) QA 6 | -------------------------------------------------------------------------------- /ios-app/Tivi/xcconfig/UITests.xcconfig: -------------------------------------------------------------------------------- 1 | DEVELOPMENT_TEAM = GLF74Y6P9T 2 | IPHONEOS_DEPLOYMENT_TARGET = 17.4 3 | PRODUCT_BUNDLE_IDENTIFIER = app.tivi.UITests 4 | PRODUCT_NAME = $(TARGET_NAME) 5 | TEST_TARGET_NAME = Tivi 6 | -------------------------------------------------------------------------------- /ios-app/Tivi/xcconfig/common/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | DEBUG_INFORMATION_FORMAT = dwarf 2 | ENABLE_PREVIEWS = YES 3 | ENABLE_TESTABILITY = YES 4 | GCC_DYNAMIC_NO_PIC = NO 5 | GCC_OPTIMIZATION_LEVEL = 0 6 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited) 7 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE 8 | KOTLIN_FRAMEWORK_BUILD_TYPE = Debug 9 | ONLY_ACTIVE_ARCH = YES 10 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG 11 | SWIFT_OPTIMIZATION_LEVEL = -Onone 12 | -------------------------------------------------------------------------------- /ios-app/Tivi/xcconfig/common/Prod.xcconfig: -------------------------------------------------------------------------------- 1 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon 2 | CODE_SIGN_IDENTITY = Apple Development 3 | CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Distribution 4 | CODE_SIGN_STYLE = Manual 5 | DEVELOPMENT_TEAM[sdk=iphoneos*] = GLF74Y6P9T 6 | PRODUCT_BUNDLE_IDENTIFIER = app.tivi.client 7 | PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*] = match AppStore app.tivi.client 8 | -------------------------------------------------------------------------------- /ios-app/Tivi/xcconfig/common/QA.xcconfig: -------------------------------------------------------------------------------- 1 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-QA 2 | CODE_SIGN_IDENTITY = Apple Development 3 | CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Distribution 4 | CODE_SIGN_STYLE = Manual 5 | DEVELOPMENT_TEAM[sdk=iphoneos*] = GLF74Y6P9T 6 | PRODUCT_BUNDLE_IDENTIFIER = app.tivi.qa 7 | PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*] = match AppStore app.tivi.qa 8 | -------------------------------------------------------------------------------- /ios-app/Tivi/xcconfig/common/Release.xcconfig: -------------------------------------------------------------------------------- 1 | DEBUG_INFORMATION_FORMAT = dwarf-with-dsym 2 | ENABLE_NS_ASSERTIONS = NO 3 | MTL_ENABLE_DEBUG_INFO = NO 4 | SWIFT_COMPILATION_MODE = wholemodule 5 | SWIFT_OPTIMIZATION_LEVEL = -O 6 | VALIDATE_PRODUCT = YES 7 | KOTLIN_FRAMEWORK_BUILD_TYPE = Release 8 | -------------------------------------------------------------------------------- /release/GoogleService-Info.plist.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/release/GoogleService-Info.plist.gpg -------------------------------------------------------------------------------- /release/app-debug.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/release/app-debug.jks -------------------------------------------------------------------------------- /release/app-release.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/release/app-release.gpg -------------------------------------------------------------------------------- /release/google-services.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/release/google-services.gpg -------------------------------------------------------------------------------- /release/play-account.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisbanes/tivi/a0c62c2c763c83e3a0ecf79b283224374bb06c4a/release/play-account.gpg -------------------------------------------------------------------------------- /shared/common/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /shared/common/src/androidMain/kotlin/app/tivi/inject/SharedActivityComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | import android.app.Activity 7 | import androidx.core.os.ConfigurationCompat 8 | import java.util.Locale 9 | import me.tatarka.inject.annotations.Provides 10 | 11 | interface SharedActivityComponent { 12 | @get:Provides 13 | val activity: Activity 14 | 15 | @Provides 16 | fun provideActivityLocale(activity: Activity): Locale { 17 | return ConfigurationCompat.getLocales(activity.resources.configuration) 18 | .get(0) ?: Locale.getDefault() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /shared/common/src/commonMain/kotlin/app/tivi/appinitializers/AppInitializers.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.appinitializers 5 | 6 | import app.tivi.core.perf.Tracer 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | class AppInitializers( 11 | private val initializers: Lazy>, 12 | private val tracer: Tracer, 13 | ) : AppInitializer { 14 | override fun initialize() { 15 | tracer.trace("AppInitializers") { 16 | for (initializer in initializers.value) { 17 | initializer.initialize() 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /shared/prod/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /shared/prod/src/androidMain/kotlin/app/tivi/inject/AndroidActivityComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | import android.app.Activity 7 | import me.tatarka.inject.annotations.Component 8 | import me.tatarka.inject.annotations.Provides 9 | 10 | @ActivityScope 11 | @Component 12 | abstract class AndroidActivityComponent( 13 | @get:Provides override val activity: Activity, 14 | @Component val applicationComponent: AndroidApplicationComponent, 15 | ) : SharedActivityComponent, 16 | ProdUiComponent { 17 | 18 | companion object 19 | } 20 | -------------------------------------------------------------------------------- /shared/prod/src/commonMain/kotlin/app/tivi/inject/ProdApplicationComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | import app.tivi.app.Flavor 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | interface ProdApplicationComponent { 10 | @Provides 11 | fun provideFlavor(): Flavor = Flavor.Standard 12 | } 13 | -------------------------------------------------------------------------------- /shared/prod/src/commonMain/kotlin/app/tivi/inject/ProdUiComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | interface ProdUiComponent : SharedUiComponent 7 | -------------------------------------------------------------------------------- /shared/prod/src/jvmMain/kotlin/app/tivi/inject/DesktopApplicationComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | import me.tatarka.inject.annotations.Component 7 | 8 | @Component 9 | @ApplicationScope 10 | abstract class DesktopApplicationComponent : 11 | SharedApplicationComponent, 12 | ProdApplicationComponent { 13 | 14 | companion object 15 | } 16 | -------------------------------------------------------------------------------- /shared/prod/src/jvmMain/kotlin/app/tivi/inject/WindowComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | import me.tatarka.inject.annotations.Component 7 | 8 | @ActivityScope 9 | @Component 10 | abstract class WindowComponent( 11 | @Component val applicationComponent: DesktopApplicationComponent, 12 | ) : ProdUiComponent { 13 | 14 | companion object 15 | } 16 | -------------------------------------------------------------------------------- /shared/qa/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /shared/qa/src/androidMain/kotlin/app/tivi/inject/AndroidActivityComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | import android.app.Activity 7 | import me.tatarka.inject.annotations.Component 8 | import me.tatarka.inject.annotations.Provides 9 | 10 | @ActivityScope 11 | @Component 12 | abstract class AndroidActivityComponent( 13 | @get:Provides override val activity: Activity, 14 | @Component val applicationComponent: AndroidApplicationComponent, 15 | ) : SharedActivityComponent, 16 | QaUiComponent { 17 | 18 | companion object 19 | } 20 | -------------------------------------------------------------------------------- /shared/qa/src/commonMain/kotlin/app/tivi/inject/QaApplicationComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | import app.tivi.app.Flavor 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | interface QaApplicationComponent { 10 | @Provides 11 | fun provideFlavor(): Flavor = Flavor.Qa 12 | } 13 | -------------------------------------------------------------------------------- /shared/qa/src/commonMain/kotlin/app/tivi/inject/QaUiComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | import app.tivi.developer.log.DevLogComponent 7 | import app.tivi.developer.notifications.DevNotificationsComponent 8 | import app.tivi.settings.developer.DevSettingsComponent 9 | 10 | interface QaUiComponent : 11 | SharedUiComponent, 12 | DevLogComponent, 13 | DevSettingsComponent, 14 | DevNotificationsComponent 15 | -------------------------------------------------------------------------------- /shared/qa/src/jvmMain/kotlin/app/tivi/inject/DesktopApplicationComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | import me.tatarka.inject.annotations.Component 7 | 8 | @Component 9 | @ApplicationScope 10 | abstract class DesktopApplicationComponent : 11 | SharedApplicationComponent, 12 | QaApplicationComponent { 13 | 14 | companion object 15 | } 16 | -------------------------------------------------------------------------------- /shared/qa/src/jvmMain/kotlin/app/tivi/inject/WindowComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.inject 5 | 6 | import me.tatarka.inject.annotations.Component 7 | 8 | @ActivityScope 9 | @Component 10 | abstract class WindowComponent( 11 | @Component val applicationComponent: DesktopApplicationComponent, 12 | ) : QaUiComponent { 13 | 14 | companion object 15 | } 16 | -------------------------------------------------------------------------------- /spotless/cb-copyright.txt: -------------------------------------------------------------------------------- 1 | // Copyright $YEAR, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | -------------------------------------------------------------------------------- /spotless/google-copyright.txt: -------------------------------------------------------------------------------- 1 | // Copyright $YEAR, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | -------------------------------------------------------------------------------- /tasks/src/androidMain/kotlin/app/tivi/tasks/BootBroadcastReceiver.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.tasks 5 | 6 | import android.content.BroadcastReceiver 7 | import android.content.Context 8 | import android.content.Intent 9 | 10 | class BootBroadcastReceiver : BroadcastReceiver() { 11 | override fun onReceive(context: Context, intent: Intent) { 12 | // We don't need to do anything here, as the AppInitializers should do what we need 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tasks/src/androidMain/kotlin/app/tivi/tasks/TasksPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2020, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.tasks 5 | 6 | import android.app.Application 7 | import androidx.work.WorkManager 8 | import app.tivi.inject.ApplicationScope 9 | import me.tatarka.inject.annotations.Provides 10 | 11 | actual interface TasksPlatformComponent { 12 | @ApplicationScope 13 | @Provides 14 | fun provideShowTasks(bind: AndroidTasks): Tasks = bind 15 | 16 | @ApplicationScope 17 | @Provides 18 | fun provideWorkManager(application: Application): WorkManager { 19 | return WorkManager.getInstance(application) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tasks/src/commonMain/kotlin/app/tivi/tasks/Tasks.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.tasks 5 | 6 | interface Tasks { 7 | fun setup() = Unit 8 | 9 | fun scheduleEpisodeNotifications() 10 | fun cancelEpisodeNotifications() 11 | 12 | fun scheduleLibrarySync() 13 | fun cancelLibrarySync() 14 | } 15 | -------------------------------------------------------------------------------- /tasks/src/commonMain/kotlin/app/tivi/tasks/TasksComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2020, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.tasks 5 | 6 | import app.tivi.appinitializers.AppInitializer 7 | import app.tivi.inject.ApplicationScope 8 | import me.tatarka.inject.annotations.IntoSet 9 | import me.tatarka.inject.annotations.Provides 10 | 11 | interface TasksComponent : TasksPlatformComponent { 12 | @ApplicationScope 13 | @Provides 14 | @IntoSet 15 | fun provideShowTasksInitializer(bind: TasksInitializer): AppInitializer = bind 16 | } 17 | 18 | expect interface TasksPlatformComponent 19 | -------------------------------------------------------------------------------- /tasks/src/iosMain/kotlin/app/tivi/tasks/TasksPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.tasks 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface TasksPlatformComponent { 10 | @ApplicationScope 11 | @Provides 12 | fun provideShowTasks(bind: IosTasks): Tasks = bind 13 | } 14 | -------------------------------------------------------------------------------- /tasks/src/jvmMain/kotlin/app/tivi/tasks/TasksPlatformComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Google LLC, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.tasks 5 | 6 | import app.tivi.inject.ApplicationScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | actual interface TasksPlatformComponent { 10 | @ApplicationScope 11 | @Provides 12 | fun provideShowTasks(): Tasks = EmptyShowTasks 13 | } 14 | 15 | object EmptyShowTasks : Tasks { 16 | override fun scheduleEpisodeNotifications() = Unit 17 | override fun cancelEpisodeNotifications() = Unit 18 | override fun scheduleLibrarySync() = Unit 19 | override fun cancelLibrarySync() = Unit 20 | } 21 | -------------------------------------------------------------------------------- /thirdparty/androidx/paging/compose/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | plugins { 6 | id("app.tivi.android.library") 7 | id("app.tivi.kotlin.multiplatform") 8 | id("app.tivi.compose") 9 | } 10 | 11 | kotlin { 12 | sourceSets { 13 | commonMain { 14 | dependencies { 15 | api(libs.paging.common) 16 | api(compose.runtime) 17 | } 18 | } 19 | } 20 | } 21 | 22 | android { 23 | namespace = "androidx.paging.compose" 24 | } 25 | -------------------------------------------------------------------------------- /thirdparty/androidx/paging/compose/src/iosMain/kotlin/PagingPlaceholders.ios.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package androidx.paging.compose 5 | 6 | internal actual fun getPagingPlaceholderKey(index: Int): Any = index 7 | -------------------------------------------------------------------------------- /thirdparty/androidx/paging/compose/src/jvmMain/kotlin/PagingPlaceholders.jvm.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package androidx.paging.compose 5 | 6 | internal actual fun getPagingPlaceholderKey(index: Int): Any = index 7 | -------------------------------------------------------------------------------- /ui/account/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/anticipated/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ui/developer/log/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/developer/log/src/commonMain/kotlin/app/tivi/developer/log/DevLogUiState.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.developer.log 5 | 6 | import androidx.compose.runtime.Immutable 7 | import app.tivi.util.LogMessage 8 | import com.slack.circuit.runtime.CircuitUiEvent 9 | import com.slack.circuit.runtime.CircuitUiState 10 | 11 | @Immutable 12 | data class DevLogUiState( 13 | val logs: List, 14 | val eventSink: (DevLogUiEvent) -> Unit, 15 | ) : CircuitUiState 16 | 17 | sealed interface DevLogUiEvent : CircuitUiEvent { 18 | data object NavigateUp : DevLogUiEvent 19 | } 20 | -------------------------------------------------------------------------------- /ui/developer/notifications/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/developer/settings/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/discover/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/episode/details/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/episode/track/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/library/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/licenses/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/popular/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/recommended/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/root/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/root/src/commonMain/kotlin/app/tivi/home/RootUiComponent.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2024, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.home 5 | 6 | import app.tivi.inject.ActivityScope 7 | import me.tatarka.inject.annotations.Provides 8 | 9 | interface RootUiComponent { 10 | @Provides 11 | @ActivityScope 12 | fun bindTiviContent(impl: DefaultTiviContent): TiviContent = impl 13 | } 14 | -------------------------------------------------------------------------------- /ui/search/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/settings/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/settings/src/androidMain/kotlin/app/tivi/settings/Platform.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.settings 5 | 6 | import android.os.Build 7 | import androidx.annotation.ChecksSdkIntAtLeast 8 | 9 | @ChecksSdkIntAtLeast(api = 31) 10 | internal actual val DynamicColorsAvailable: Boolean = Build.VERSION.SDK_INT >= 31 11 | internal actual val OpenSourceLicenseAvailable: Boolean = true 12 | -------------------------------------------------------------------------------- /ui/settings/src/commonMain/kotlin/app/tivi/settings/Platform.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.settings 5 | 6 | internal expect val DynamicColorsAvailable: Boolean 7 | internal expect val OpenSourceLicenseAvailable: Boolean 8 | -------------------------------------------------------------------------------- /ui/settings/src/iosMain/kotlin/app/tivi/settings/Platform.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.settings 5 | 6 | internal actual val DynamicColorsAvailable: Boolean = false 7 | 8 | internal actual val OpenSourceLicenseAvailable: Boolean = true 9 | -------------------------------------------------------------------------------- /ui/settings/src/jvmMain/kotlin/app/tivi/settings/Platform.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Christopher Banes and the Tivi project contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package app.tivi.settings 5 | 6 | internal actual val DynamicColorsAvailable: Boolean = false 7 | internal actual val OpenSourceLicenseAvailable: Boolean = false 8 | -------------------------------------------------------------------------------- /ui/show/details/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/show/seasons/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/trending/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/upnext/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | --------------------------------------------------------------------------------