├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── font
│ │ │ │ └── lato_black.ttf
│ │ │ ├── drawable
│ │ │ │ ├── ic_movplay.png
│ │ │ │ ├── ic_launcher_foreground.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── values
│ │ │ │ ├── themes.xml
│ │ │ │ └── colors.xml
│ │ │ ├── xml
│ │ │ │ ├── backup_rules.xml
│ │ │ │ └── data_extraction_rules.xml
│ │ │ └── drawable-v24
│ │ │ │ ├── ic_facebook.xml
│ │ │ │ ├── ic_youtube.xml
│ │ │ │ ├── ic_instagram.xml
│ │ │ │ ├── ic_outline_no_photography_24.xml
│ │ │ │ └── ic_twitter.xml
│ │ ├── ic_launcher-playstore.png
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── movplayv3
│ │ │ │ ├── data
│ │ │ │ ├── model
│ │ │ │ │ ├── SortOrder.kt
│ │ │ │ │ ├── RelationType.kt
│ │ │ │ │ ├── movie
│ │ │ │ │ │ ├── MovieType.kt
│ │ │ │ │ │ ├── MovieCollection.kt
│ │ │ │ │ │ ├── MoviesRemoteKeys.kt
│ │ │ │ │ │ ├── MovieDetailsRemoteKey.kt
│ │ │ │ │ │ ├── MovieWatchProviderType.kt
│ │ │ │ │ │ ├── MoviesResponse.kt
│ │ │ │ │ │ ├── RecentlyBrowsedMovie.kt
│ │ │ │ │ │ ├── MovieFavorite.kt
│ │ │ │ │ │ ├── MovieEntity.kt
│ │ │ │ │ │ ├── MovieDetailEntity.kt
│ │ │ │ │ │ ├── Movie.kt
│ │ │ │ │ │ └── MovieStatus.kt
│ │ │ │ │ ├── DateRange.kt
│ │ │ │ │ ├── tvshow
│ │ │ │ │ │ ├── TvShowType.kt
│ │ │ │ │ │ ├── TvShowDetailsRemoteKey.kt
│ │ │ │ │ │ ├── TvShowsRemoteKeys.kt
│ │ │ │ │ │ ├── TvShowsResponse.kt
│ │ │ │ │ │ ├── TvShowFavorite.kt
│ │ │ │ │ │ ├── RecentlyBrowsedTvShow.kt
│ │ │ │ │ │ ├── TvSeasonsResponse.kt
│ │ │ │ │ │ ├── TvShowEntity.kt
│ │ │ │ │ │ ├── TvShowDetailsEntity.kt
│ │ │ │ │ │ ├── TvShowStatus.kt
│ │ │ │ │ │ ├── TvShow.kt
│ │ │ │ │ │ └── TvType.kt
│ │ │ │ │ ├── GenresResponse.kt
│ │ │ │ │ ├── VoteRange.kt
│ │ │ │ │ ├── Genre.kt
│ │ │ │ │ ├── Credits.kt
│ │ │ │ │ ├── Member.kt
│ │ │ │ │ ├── SeasonInfo.kt
│ │ │ │ │ ├── ImagesResponse.kt
│ │ │ │ │ ├── PresentableItemState.kt
│ │ │ │ │ ├── DateParam.kt
│ │ │ │ │ ├── GenresParam.kt
│ │ │ │ │ ├── SpokenLanguage.kt
│ │ │ │ │ ├── DetailPresentableItemState.kt
│ │ │ │ │ ├── ProductionCountry.kt
│ │ │ │ │ ├── WatchProvidersParam.kt
│ │ │ │ │ ├── DeviceLanguage.kt
│ │ │ │ │ ├── Config.kt
│ │ │ │ │ ├── MediaType.kt
│ │ │ │ │ ├── Network.kt
│ │ │ │ │ ├── FavoriteType.kt
│ │ │ │ │ ├── SearchQuery.kt
│ │ │ │ │ ├── SnackBarEvent.kt
│ │ │ │ │ ├── AuthorDetails.kt
│ │ │ │ │ ├── ReviewsResponse.kt
│ │ │ │ │ ├── Presentable.kt
│ │ │ │ │ ├── SearchResponse.kt
│ │ │ │ │ ├── ProductionCompany.kt
│ │ │ │ │ ├── ShareDetails.kt
│ │ │ │ │ ├── Collection.kt
│ │ │ │ │ ├── Image.kt
│ │ │ │ │ ├── Creator.kt
│ │ │ │ │ ├── Review.kt
│ │ │ │ │ ├── CollectionResponse.kt
│ │ │ │ │ ├── SortTypeParam.kt
│ │ │ │ │ ├── Season.kt
│ │ │ │ │ ├── ImagesConfig.kt
│ │ │ │ │ ├── SearchResult.kt
│ │ │ │ │ ├── CrewMember.kt
│ │ │ │ │ ├── SeasonDetails.kt
│ │ │ │ │ ├── ExternalId.kt
│ │ │ │ │ ├── CastMember.kt
│ │ │ │ │ ├── PersonDetails.kt
│ │ │ │ │ ├── Part.kt
│ │ │ │ │ ├── WatchProviders.kt
│ │ │ │ │ ├── Episode.kt
│ │ │ │ │ ├── ExternalIds.kt
│ │ │ │ │ ├── VideosResponse.kt
│ │ │ │ │ ├── SortType.kt
│ │ │ │ │ └── CombinedCredits.kt
│ │ │ │ ├── remote
│ │ │ │ │ └── api
│ │ │ │ │ │ ├── ApiError.kt
│ │ │ │ │ │ ├── ApiParams.kt
│ │ │ │ │ │ └── others
│ │ │ │ │ │ └── TmdbOthersApiHelper.kt
│ │ │ │ ├── initializer
│ │ │ │ │ ├── AppInitializer.kt
│ │ │ │ │ ├── FirebaseInitializer.kt
│ │ │ │ │ ├── ConfigDataSourceInitializer.kt
│ │ │ │ │ └── AppInitializers.kt
│ │ │ │ ├── local
│ │ │ │ │ └── db
│ │ │ │ │ │ ├── SearchQueryDao.kt
│ │ │ │ │ │ ├── movie
│ │ │ │ │ │ ├── MoviesDetailsRemoteKeysDao.kt
│ │ │ │ │ │ ├── MoviesDetailsDao.kt
│ │ │ │ │ │ ├── MoviesDao.kt
│ │ │ │ │ │ ├── MoviesRemoteKeysDao.kt
│ │ │ │ │ │ └── FavoritesMoviesDao.kt
│ │ │ │ │ │ └── tvshow
│ │ │ │ │ │ ├── TvShowsDetailsRemoteKeysDao.kt
│ │ │ │ │ │ ├── TvShowsDetailsDao.kt
│ │ │ │ │ │ ├── TvShowsRemoteKeysDao.kt
│ │ │ │ │ │ ├── TvShowsDao.kt
│ │ │ │ │ │ └── FavoritesTvShowsDao.kt
│ │ │ │ └── repository
│ │ │ │ │ ├── search
│ │ │ │ │ └── SearchRepository.kt
│ │ │ │ │ ├── person
│ │ │ │ │ ├── PersonRepository.kt
│ │ │ │ │ └── PersonRepositoryImpl.kt
│ │ │ │ │ ├── browsed
│ │ │ │ │ └── RecentlyBrowsedRepository.kt
│ │ │ │ │ ├── config
│ │ │ │ │ └── ConfigRepository.kt
│ │ │ │ │ ├── favorites
│ │ │ │ │ └── FavoritesRepository.kt
│ │ │ │ │ └── season
│ │ │ │ │ └── SeasonRepository.kt
│ │ │ │ ├── ui
│ │ │ │ ├── screens
│ │ │ │ │ ├── details
│ │ │ │ │ │ ├── movie
│ │ │ │ │ │ │ └── MovieDetailsScreenArgs.kt
│ │ │ │ │ │ ├── person
│ │ │ │ │ │ │ ├── PersonDetailsScreenArgs.kt
│ │ │ │ │ │ │ └── PersonDetailsScreenUIState.kt
│ │ │ │ │ │ └── tvshow
│ │ │ │ │ │ │ └── TvShowDetailsScreenArgs.kt
│ │ │ │ │ ├── seasons
│ │ │ │ │ │ ├── SeasonDetailsScreenArgs.kt
│ │ │ │ │ │ └── SeasonDetailsScreenUiState.kt
│ │ │ │ │ ├── browse
│ │ │ │ │ │ ├── movies
│ │ │ │ │ │ │ ├── BrowseMoviesScreenArgs.kt
│ │ │ │ │ │ │ └── BrowseMoviesScreenUIState.kt
│ │ │ │ │ │ └── tvshows
│ │ │ │ │ │ │ ├── BrowseTvShowsScreenArgs.kt
│ │ │ │ │ │ │ └── BrowseTvShowsScreenUIState.kt
│ │ │ │ │ ├── reviews
│ │ │ │ │ │ ├── ReviewsScreenNavArgs.kt
│ │ │ │ │ │ ├── ReviewsScreenUiState.kt
│ │ │ │ │ │ └── ReviewsViewModel.kt
│ │ │ │ │ ├── related
│ │ │ │ │ │ ├── movies
│ │ │ │ │ │ │ ├── RelatedMoviesScreenArgs.kt
│ │ │ │ │ │ │ └── RelatedMoviesScreenUiState.kt
│ │ │ │ │ │ └── tvseries
│ │ │ │ │ │ │ ├── RelatedTvShowScreenArgs.kt
│ │ │ │ │ │ │ └── RelatedTvShowScreenUiState.kt
│ │ │ │ │ ├── scanner
│ │ │ │ │ │ └── ScannerScreenUIState.kt
│ │ │ │ │ ├── favorite
│ │ │ │ │ │ ├── FavoritesScreenUIState.kt
│ │ │ │ │ │ └── FavoritesScreenViewModel.kt
│ │ │ │ │ └── search
│ │ │ │ │ │ └── SearchScreenUIState.kt
│ │ │ │ ├── components
│ │ │ │ │ ├── items
│ │ │ │ │ │ ├── MovplayLoadingPresentableItem.kt
│ │ │ │ │ │ ├── MovplayErrorPresentableItem.kt
│ │ │ │ │ │ ├── MovplayNoPhotoPresentableItem.kt
│ │ │ │ │ │ └── MovplayReviewItemPlaceHolder.kt
│ │ │ │ │ ├── others
│ │ │ │ │ │ ├── MovplaySectionDivider.kt
│ │ │ │ │ │ ├── MovplayPosterPlaceholder.kt
│ │ │ │ │ │ ├── MovplayAnimatedContentContainer.kt
│ │ │ │ │ │ └── MovplayLabeledSwitch.kt
│ │ │ │ │ ├── chips
│ │ │ │ │ │ ├── MovplayGenreChip.kt
│ │ │ │ │ │ └── MovplayAdultChips.kt
│ │ │ │ │ ├── texts
│ │ │ │ │ │ ├── MovplayInfoText.kt
│ │ │ │ │ │ ├── MovplaySectionLabel.kt
│ │ │ │ │ │ ├── MovplayAdditionalInfoText.kt
│ │ │ │ │ │ └── MovplayLabeledText.kt
│ │ │ │ │ ├── button
│ │ │ │ │ │ ├── MovplayFilterFloatingButton.kt
│ │ │ │ │ │ ├── MovplayScrollToStartButton.kt
│ │ │ │ │ │ ├── MovplayScrollToTopButton.kt
│ │ │ │ │ │ └── MovplayBackButton.kt
│ │ │ │ │ ├── sections
│ │ │ │ │ │ └── MovplayGenresSection.kt
│ │ │ │ │ ├── dialogs
│ │ │ │ │ │ ├── MovplatErrorDialog.kt
│ │ │ │ │ │ ├── MovplayInfoDialog.kt
│ │ │ │ │ │ └── MovplayExitDialog.kt
│ │ │ │ │ ├── lists
│ │ │ │ │ │ └── MovplayProvidersSourceList.kt
│ │ │ │ │ └── dropdowns
│ │ │ │ │ │ └── MovplaySortTypeDropdown.kt
│ │ │ │ └── theme
│ │ │ │ │ ├── Color.kt
│ │ │ │ │ ├── Spacing.kt
│ │ │ │ │ ├── Sizes.kt
│ │ │ │ │ └── Type.kt
│ │ │ │ ├── domain
│ │ │ │ └── usecase
│ │ │ │ │ ├── MediaSearchQueriesUseCaseImpl.kt
│ │ │ │ │ ├── GetCameraAvailableUseCaseImpl.kt
│ │ │ │ │ ├── GetSpeechToTextAvailableUseCaseImpl.kt
│ │ │ │ │ ├── movie
│ │ │ │ │ ├── ClearRecentlyBrowsedMoviesUseCaseImpl.kt
│ │ │ │ │ ├── GetFavoriteMoviesIdsUseCaseImpl.kt
│ │ │ │ │ ├── LikeMovieUseCaseImpl.kt
│ │ │ │ │ ├── GetFavoritesMovieCountUseCaseImpl.kt
│ │ │ │ │ ├── UnlikeMovieUseCaseImpl.kt
│ │ │ │ │ ├── GetMovieGenresUseCaseImpl.kt
│ │ │ │ │ ├── AddRecentlyBrowsedMovieUseCaseImpl.kt
│ │ │ │ │ ├── GetAllMoviesWatchProvidersUseCaseImpl.kt
│ │ │ │ │ ├── GetFavoritesMoviesUseCaseImpl.kt
│ │ │ │ │ ├── GetRecentlyBrowsedMoviesUseCaseImpl.kt
│ │ │ │ │ ├── GetMovieCreditUseCaseImpl.kt
│ │ │ │ │ ├── GetMovieDetailsUseCaseImpl.kt
│ │ │ │ │ ├── GetOtherDirectorMoviesUseCaseImpl.kt
│ │ │ │ │ ├── GetUpcomingMoviesUseCaseImpl.kt
│ │ │ │ │ ├── GetDiscoverAllMoviesUseCaseImpl.kt
│ │ │ │ │ ├── GetTopRatedMoviesUseCaseImpl.kt
│ │ │ │ │ ├── GetTrendingMoviesUseCaseImpl.kt
│ │ │ │ │ ├── GetPopularMoviesUseCaseImpl.kt
│ │ │ │ │ ├── GetMovieReviewsCountUseCaseImpl.kt
│ │ │ │ │ ├── GetMovieBackdropsUseCaseImpl.kt
│ │ │ │ │ ├── GetMovieExternalIdsUseCaseImpl.kt
│ │ │ │ │ ├── GetRelatedMoviesOfTypeUseCaseImpl.kt
│ │ │ │ │ └── GetMovieWatchProvidersUseCaseImpl.kt
│ │ │ │ │ ├── tvshow
│ │ │ │ │ ├── ClearRecentlyBrowsedTvShowsUseCaseImpl.kt
│ │ │ │ │ ├── GetFavoriteTvShowsCountUseCaseImpl.kt
│ │ │ │ │ ├── GetFavoriteTvShowIdsUseCaseImpl.kt
│ │ │ │ │ ├── LikeTvShowUseCaseImpl.kt
│ │ │ │ │ ├── UnlikeTvShowUseCaseImpl.kt
│ │ │ │ │ ├── GetTvShowGenresUseCaseImpl.kt
│ │ │ │ │ ├── AddRecentlyBrowsedTvShowUseCaseImpl.kt
│ │ │ │ │ ├── GetNextEpisodeDaysRemainingUseCaseImpl.kt
│ │ │ │ │ ├── GetAllTvShowsWatchProvidersUseCaseImpl.kt
│ │ │ │ │ ├── GetFavoritesTvShowsUseCaseImpl.kt
│ │ │ │ │ ├── GetRecentlyBrowsedTvShowsUseCaseImpl.kt
│ │ │ │ │ ├── GetTvShowDetailsUseCaseImpl.kt
│ │ │ │ │ ├── GetTopRatedTvShowsUseCaseImpl.kt
│ │ │ │ │ ├── GetTrendingTvShowsUseCaseImpl.kt
│ │ │ │ │ ├── GetAiringTodayTvShowsUseCaseImpl.kt
│ │ │ │ │ ├── GetDiscoverAllTvShowsUseCaseImpl.kt
│ │ │ │ │ ├── GetSeasonDetailsUseCaseImpl.kt
│ │ │ │ │ ├── GetSeasonCreditsUseCaseImpl.kt
│ │ │ │ │ ├── GetTvShowReviewsCountUseCaseImpl.kt
│ │ │ │ │ ├── GetTvShowImagesUseCaseImpl.kt
│ │ │ │ │ ├── GetTvShowExternalIdsUseCaseImpl.kt
│ │ │ │ │ ├── GetRelatedTvShowsOfTypeUseCaseImpl.kt
│ │ │ │ │ ├── GetTvShowWatchProvidersUseCaseImpl.kt
│ │ │ │ │ └── GetTvShowVideosUseCaseImpl.kt
│ │ │ │ │ ├── MediaAddSearchQueryUseCaseImpl.kt
│ │ │ │ │ ├── GetDeviceLanguageUseCaseImpl.kt
│ │ │ │ │ ├── ScanBitmapForTextUseCaseImpl.kt
│ │ │ │ │ ├── GetMediaMultiSearchUseCaseImpl.kt
│ │ │ │ │ ├── GetPersonExternalIdsUseCaseImpl.kt
│ │ │ │ │ ├── GetPersonDetailsUseCaseImpl.kt
│ │ │ │ │ ├── GetCombinedCreditsUseCaseImpl.kt
│ │ │ │ │ ├── GetMediaTypeReviewsUseCaseImpl.kt
│ │ │ │ │ ├── GetFavoritesUseCaseImpl.kt
│ │ │ │ │ └── GetEpisodeStillsUseCaseImpl.kt
│ │ │ │ ├── di
│ │ │ │ ├── HelperBinds.kt
│ │ │ │ └── AppModuleBinds.kt
│ │ │ │ ├── MovplayV3Application.kt
│ │ │ │ ├── utils
│ │ │ │ ├── NavigationUtils.kt
│ │ │ │ ├── Formatter.kt
│ │ │ │ ├── Converters.kt
│ │ │ │ └── ColorUtils.kt
│ │ │ │ └── BaseViewModel.kt
│ │ └── AndroidManifest.xml
│ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── movplayv3
│ │ │ ├── api
│ │ │ └── TmdbApiTesting.kt
│ │ │ └── ExampleInstrumentedTest.kt
│ └── test
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── movplayv3
│ │ └── ExampleUnitTest.kt
└── proguard-rules.pro
├── screenshots
├── nothing
├── movie.png
├── show.png
├── jetpack.png
├── kotlin.png
├── search.png
├── studio.png
├── summary.png
└── movColored.png
├── .idea
├── .gitignore
├── compiler.xml
├── vcs.xml
├── kotlinScripting.xml
├── deploymentTargetDropDown.xml
├── gradle.xml
└── misc.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle.kts
└── gradle.properties
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/screenshots/nothing:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/screenshots/movie.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/screenshots/movie.png
--------------------------------------------------------------------------------
/screenshots/show.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/screenshots/show.png
--------------------------------------------------------------------------------
/screenshots/jetpack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/screenshots/jetpack.png
--------------------------------------------------------------------------------
/screenshots/kotlin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/screenshots/kotlin.png
--------------------------------------------------------------------------------
/screenshots/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/screenshots/search.png
--------------------------------------------------------------------------------
/screenshots/studio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/screenshots/studio.png
--------------------------------------------------------------------------------
/screenshots/summary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/screenshots/summary.png
--------------------------------------------------------------------------------
/screenshots/movColored.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/screenshots/movColored.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/font/lato_black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/font/lato_black.ttf
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_movplay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/drawable/ic_movplay.png
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/movplayv3/api/TmdbApiTesting.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.api
2 |
3 | class TmdbApiTesting {
4 | }
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/SortOrder.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | enum class SortOrder {
4 | Asc, Desc
5 | }
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aldikitta/MovplayV3/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/RelationType.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | enum class RelationType {
4 | Similar, Recommended
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/MovieType.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | enum class MovieType {
4 | Upcoming, TopRated, Favorite, RecentlyBrowsed, Trending, NowPlaying
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/DateRange.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import java.util.*
4 |
5 | data class DateRange(
6 | val from: Date? = null,
7 | val to: Date? = null
8 | )
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/tvshow/TvShowType.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.tvshow
2 |
3 | enum class TvShowType {
4 | TopRated, AiringToday, Favorite, RecentlyBrowsed, Trending, OnTheAir
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/remote/api/ApiError.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.remote.api
2 |
3 | data class ApiError(
4 | val errorCode: Int,
5 | val statusCode: Int?,
6 | val statusMessage: String?
7 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/initializer/AppInitializer.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.initializer
2 |
3 | import android.app.Application
4 |
5 | interface AppInitializer {
6 | fun init(application: Application)
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/GenresResponse.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.JsonClass
4 |
5 | @JsonClass(generateAdapter = true)
6 | data class GenresResponse(
7 | val genres: List
8 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/VoteRange.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | data class VoteRange(
4 | val default: ClosedFloatingPointRange = 0f..10f,
5 | val current: ClosedFloatingPointRange = default
6 | )
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Genre.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.JsonClass
4 |
5 | @JsonClass(generateAdapter = true)
6 | data class Genre(
7 | val id: Int,
8 | val name: String
9 | )
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/MovieCollection.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | import com.example.movplayv3.data.model.Part
4 |
5 | data class MovieCollection(
6 | val name: String,
7 | val parts: List
8 | )
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Jul 17 23:02:06 SGT 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Credits.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.JsonClass
4 |
5 | @JsonClass(generateAdapter = true)
6 | data class Credits(
7 | val cast: List?,
8 | val crew: List?
9 | )
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Member.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import androidx.compose.runtime.Stable
4 |
5 | @Stable
6 | interface Member {
7 | val id: Int
8 | val profilePath: String?
9 | val firstLine: String?
10 | val secondLine: String?
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/SeasonInfo.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | data class SeasonInfo(
8 | val tvSeriesId: Int,
9 | val seasonNumber: Int
10 | ) : Parcelable
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/ImagesResponse.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.JsonClass
4 |
5 | @JsonClass(generateAdapter = true)
6 | data class ImagesResponse(
7 | val id: Int,
8 | val backdrops: List?,
9 | val stills: List?
10 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/PresentableItemState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | sealed class PresentableItemState {
4 | object Loading : PresentableItemState()
5 | object Error : PresentableItemState()
6 | data class Result(val presentable: Presentable) : PresentableItemState()
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/DateParam.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.example.movplayv3.utils.formatted
4 | import java.util.*
5 |
6 | data class DateParam(private val date: Date) {
7 | override fun toString(): String {
8 | return date.formatted("yyyy-MM-dd")
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/GenresParam.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | data class GenresParam(private val genres: List) {
4 | override fun toString(): String {
5 | return genres.distinct().map { genre ->
6 | genre.id
7 | }.joinToString(separator = "|")
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/details/movie/MovieDetailsScreenArgs.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.details.movie
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | data class MovieDetailsScreenArgs(
8 | val movieId: Int,
9 | val startRoute: String
10 | ) : Parcelable
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/SpokenLanguage.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class SpokenLanguage(
8 | @Json(name = "iso_639_1")
9 | val iso: String,
10 | val name: String
11 | )
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/details/person/PersonDetailsScreenArgs.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.details.person
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | data class PersonDetailsScreenArgs(
8 | val personId: Int,
9 | val startRoute: String
10 | ) : Parcelable
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/details/tvshow/TvShowDetailsScreenArgs.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.details.tvshow
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | data class TvShowDetailsScreenArgs(
8 | val tvShowId: Int,
9 | val startRoute: String
10 | ): Parcelable
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/DetailPresentableItemState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | sealed class DetailPresentableItemState {
4 | object Loading : DetailPresentableItemState()
5 | object Error : DetailPresentableItemState()
6 | data class Result(val presentable: DetailPresentable) : DetailPresentableItemState()
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/ProductionCountry.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class ProductionCountry(
8 | @Json(name = "iso_3166_1")
9 | val iso: String,
10 | val name: String
11 | )
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/seasons/SeasonDetailsScreenArgs.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.seasons
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | data class SeasonDetailsScreenArgs(
8 | val tvShowId: Int,
9 | val seasonNumber: Int,
10 | val startRoute: String
11 | ) : Parcelable
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/WatchProvidersParam.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | data class WatchProvidersParam(private val watchProviders: List) {
4 | override fun toString(): String {
5 | return watchProviders.distinct().map { provider -> provider.providerId }
6 | .joinToString(separator = "|")
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/browse/movies/BrowseMoviesScreenArgs.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.browse.movies
2 |
3 | import android.os.Parcelable
4 | import com.example.movplayv3.data.model.movie.MovieType
5 | import kotlinx.parcelize.Parcelize
6 |
7 | @Parcelize
8 | data class BrowseMoviesScreenArgs(
9 | val movieType: MovieType
10 | ) : Parcelable
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/DeviceLanguage.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | data class DeviceLanguage(
4 | val region: String,
5 | val languageCode: String
6 | ) {
7 | companion object {
8 | val default: DeviceLanguage = DeviceLanguage(
9 | region = "US",
10 | languageCode = "en-US"
11 | )
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/browse/tvshows/BrowseTvShowsScreenArgs.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.browse.tvshows
2 |
3 | import android.os.Parcelable
4 | import com.example.movplayv3.data.model.tvshow.TvShowType
5 | import kotlinx.parcelize.Parcelize
6 |
7 | @Parcelize
8 | data class BrowseTvShowsScreenArgs(
9 | val tvShowType: TvShowType
10 | ) : Parcelable
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Config.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class Config(
8 | @Json(name = "images")
9 | val imagesConfig: ImagesConfig,
10 | @Json(name = "change_keys")
11 | val changeKeys: List
12 | )
13 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 | rootProject.name = "MovplayV3"
16 | include(":app")
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/MoviesRemoteKeys.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity
7 | data class MoviesRemoteKeys(
8 | @PrimaryKey(autoGenerate = false)
9 | val language: String,
10 | val type: MovieEntityType,
11 | val nextPage: Int?,
12 | val lastUpdated: Long
13 | )
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/reviews/ReviewsScreenNavArgs.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.reviews
2 |
3 | import android.os.Parcelable
4 | import com.example.movplayv3.data.model.MediaType
5 | import kotlinx.parcelize.Parcelize
6 |
7 | @Parcelize
8 | data class ReviewsScreenNavArgs(
9 | val startRoute: String,
10 | val mediaId: Int,
11 | val type: MediaType
12 | ) : Parcelable
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/initializer/FirebaseInitializer.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.initializer
2 |
3 | import android.app.Application
4 | import com.google.firebase.FirebaseApp
5 | import javax.inject.Inject
6 |
7 | class FirebaseInitializer @Inject constructor() : AppInitializer {
8 | override fun init(application: Application) {
9 | FirebaseApp.initializeApp(application)
10 | }
11 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/.idea/kotlinScripting.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 2147483647
6 | true
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/MediaType.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = false)
7 | enum class MediaType(val value: String) {
8 | @Json(name = "movie")
9 | Movie("movie"),
10 |
11 | @Json(name = "tv")
12 | Tv("tv"),
13 |
14 | @Json(name = "person")
15 | Person("person")
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Network.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class Network(
8 | val id: Int,
9 | val name: String,
10 | @Json(name = "logo_path")
11 | val logoPath: String?,
12 | @Json(name = "origin_country")
13 | val originCountry: String
14 | )
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/related/movies/RelatedMoviesScreenArgs.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.related.movies
2 |
3 | import android.os.Parcelable
4 | import com.example.movplayv3.data.model.RelationType
5 | import kotlinx.parcelize.Parcelize
6 |
7 | @Parcelize
8 | data class RelatedMoviesScreenArgs(
9 | val movieId: Int,
10 | val type: RelationType,
11 | val startRoute: String
12 | ) : Parcelable
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/FavoriteType.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import androidx.annotation.StringRes
4 | import com.example.movplayv3.R
5 |
6 | enum class FavoriteType {
7 | Movie, TvShow;
8 |
9 | @StringRes
10 | fun getLabelResourceId() = when (this) {
11 | Movie -> R.string.favourite_type_movie_label
12 | TvShow -> R.string.favourite_type_tv_show_label
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/SearchQuery.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import java.util.*
7 |
8 | @Entity
9 | data class SearchQuery(
10 | @PrimaryKey
11 | @ColumnInfo(index = true)
12 | val query: String,
13 | @ColumnInfo(name = "last_use_date")
14 | val lastUseDate: Date
15 | )
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/related/tvseries/RelatedTvShowScreenArgs.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.related.tvseries
2 |
3 | import android.os.Parcelable
4 | import com.example.movplayv3.data.model.RelationType
5 | import kotlinx.parcelize.Parcelize
6 |
7 | @Parcelize
8 | data class RelatedTvShowScreenArgs(
9 | val tvShowId: Int,
10 | val type: RelationType,
11 | val startRoute: String
12 | ) : Parcelable
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/SnackBarEvent.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import androidx.annotation.StringRes
4 | import com.example.movplayv3.R
5 |
6 | sealed class SnackBarEvent(@StringRes val messageStringRes: Int) {
7 | object NetworkDisconnected : SnackBarEvent(R.string.snack_bar_network_disconnected_label)
8 | object NetworkConnected : SnackBarEvent(R.string.snack_bar_network_connected_label)
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/MovieDetailsRemoteKey.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | import androidx.room.Entity
4 | import androidx.room.Index
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity(indices = [Index(value = ["language"])])
8 | data class MovieDetailsRemoteKey(
9 | @PrimaryKey(autoGenerate = false)
10 | val language: String,
11 | val nextPage: Int?,
12 | val lastUpdates: Long
13 | )
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/tvshow/TvShowDetailsRemoteKey.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.tvshow
2 |
3 | import androidx.room.Entity
4 | import androidx.room.Index
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity(indices = [Index(value = ["language"])])
8 | data class TvShowDetailsRemoteKey(
9 | @PrimaryKey(autoGenerate = false)
10 | val language: String,
11 | val nextPage: Int?,
12 | val lastUpdated: Long
13 | )
14 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/movplayv3/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/AuthorDetails.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class AuthorDetails(
8 | val name: String,
9 | val username: String,
10 | @Json(name = "avatar_path")
11 | val avatarPath: String?,
12 | val rating: Float?,
13 | @Transient
14 | val avatarUrl: String? = null
15 | )
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/MovieWatchProviderType.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | import com.example.movplayv3.R
4 |
5 | enum class MovieWatchProviderType {
6 | Stream, Rent, Buy;
7 |
8 | fun getLabelResId() = when (this) {
9 | Rent -> R.string.movie_provider_type_rent_label
10 | Buy -> R.string.movie_provider_type_buy_label
11 | Stream -> R.string.movie_provider_type_stream_label
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/items/MovplayLoadingPresentableItem.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.items
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import com.example.movplayv3.ui.components.others.PosterPlaceholder
6 |
7 | @Composable
8 | fun MovplayLoadingPresentableItem(
9 | modifier: Modifier = Modifier,
10 | ) {
11 | PosterPlaceholder(
12 | modifier = modifier
13 | )
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/ReviewsResponse.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class ReviewsResponse(
8 | val id: Int,
9 | val results: List,
10 | val page: Int,
11 | @Json(name = "total_pages")
12 | val totalPages: Int,
13 | @Json(name = "total_results")
14 | val totalResults: Int
15 | )
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/remote/api/ApiParams.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.remote.api
2 |
3 | import kotlin.time.Duration.Companion.seconds
4 |
5 | object ApiParams {
6 | const val secureBaseUrl = "https://api.themoviedb.org/3/"
7 |
8 | //10 MB cache
9 | const val cacheSize = (10 * 1024 * 1024).toLong()
10 |
11 | object Timeouts {
12 | val connect = 10.seconds
13 | val write = 10.seconds
14 | val read = 10.seconds
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Presentable.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import androidx.compose.runtime.Stable
4 |
5 | @Stable
6 | interface Presentable {
7 | val id: Int
8 | val title: String
9 | val posterPath: String?
10 | }
11 |
12 | @Stable
13 | interface DetailPresentable : Presentable {
14 | val adult: Boolean?
15 | val overview: String?
16 | val backdropPath: String?
17 | val voteAverage: Float
18 | val voteCount: Int
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/SearchResponse.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class SearchResponse(
8 | val page: Int,
9 | @Json(name = "results")
10 | val results: List,
11 | @Json(name = "total_pages")
12 | val totalPages: Int,
13 | @Json(name = "total_results")
14 | val totalResults: Int
15 | )
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/MoviesResponse.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class MoviesResponse(
8 | val page: Int,
9 | @Json(name = "results")
10 | val movies: List,
11 | @Json(name = "total_pages")
12 | val totalPages: Int,
13 | @Json(name = "total_results")
14 | val totalResults: Int
15 | )
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/tvshow/TvShowsRemoteKeys.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.tvshow
2 |
3 | import androidx.room.Entity
4 | import androidx.room.Index
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity(indices = [Index(value = ["language", "type"])])
8 | data class TvShowsRemoteKeys(
9 | @PrimaryKey(autoGenerate = false)
10 | val language: String,
11 | val type: TvShowEntityType,
12 | val nextPage: Int?,
13 | val lastUpdated: Long
14 | )
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/MediaSearchQueriesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import com.example.movplayv3.data.repository.search.SearchRepository
4 | import javax.inject.Inject
5 |
6 | class MediaSearchQueriesUseCaseImpl @Inject constructor(
7 | private val searchRepository: SearchRepository
8 | ) {
9 | suspend operator fun invoke(query: String): List {
10 | return searchRepository.searchQueries(query)
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/others/MovplaySectionDivider.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.others
2 |
3 | import androidx.compose.material3.Divider
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.unit.Dp
7 |
8 | @Composable
9 | fun MovplaySectionDivider(
10 | modifier: Modifier = Modifier
11 | ) {
12 | Divider(
13 | modifier = modifier,
14 | thickness = Dp.Hairline
15 | )
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/initializer/ConfigDataSourceInitializer.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.initializer
2 |
3 | import android.app.Application
4 | import com.example.movplayv3.data.paging.ConfigDataSource
5 | import javax.inject.Inject
6 |
7 | class ConfigDataSourceInitializer @Inject constructor(
8 | private val configDataSource: ConfigDataSource
9 | ) : AppInitializer {
10 | override fun init(application: Application) {
11 | configDataSource.init()
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/tvshow/TvShowsResponse.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.tvshow
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class TvShowsResponse(
8 | val page: Int,
9 | @Json(name = "results")
10 | val tvShows: List,
11 | @Json(name = "total_pages")
12 | val totalPages: Int,
13 | @Json(name = "total_results")
14 | val totalResults: Int
15 | )
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/ProductionCompany.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class ProductionCompany(
8 | val name: String,
9 | val id: Int,
10 | @Json(name = "logo_path")
11 | val logoPath: String?,
12 | @Json(name = "origin_country")
13 | val originCountry: String,
14 | @Transient
15 | val logoUrl: String? = null
16 | )
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/others/MovplayPosterPlaceholder.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.others
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import com.example.movplayv3.utils.defaultPlaceholder
7 |
8 | @Composable
9 | fun PosterPlaceholder(
10 | modifier: Modifier = Modifier,
11 | ) {
12 | Box(
13 | modifier = modifier.defaultPlaceholder()
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/GetCameraAvailableUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import com.example.movplayv3.data.repository.config.ConfigRepository
4 | import kotlinx.coroutines.flow.Flow
5 | import javax.inject.Inject
6 |
7 | class GetCameraAvailableUseCaseImpl @Inject constructor(
8 | private val configRepository: ConfigRepository
9 | ) {
10 | operator fun invoke(): Flow {
11 | return configRepository.getCameraAvailable()
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/GetSpeechToTextAvailableUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import com.example.movplayv3.data.repository.config.ConfigRepository
4 | import kotlinx.coroutines.flow.Flow
5 | import javax.inject.Inject
6 |
7 | class GetSpeechToTextAvailableUseCaseImpl @Inject constructor(
8 | private val configRepository: ConfigRepository
9 | ) {
10 | operator fun invoke(): Flow {
11 | return configRepository.getSpeechToTextAvailable()
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/ClearRecentlyBrowsedMoviesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.repository.browsed.RecentlyBrowsedRepository
4 | import javax.inject.Inject
5 |
6 | class ClearRecentlyBrowsedMoviesUseCaseImpl @Inject constructor(
7 | private val recentlyBrowsedRepository: RecentlyBrowsedRepository
8 | ) {
9 | operator fun invoke() {
10 | return recentlyBrowsedRepository.clearRecentlyBrowsedMovies()
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/initializer/AppInitializers.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.initializer
2 |
3 | import android.app.Application
4 | import javax.inject.Inject
5 | import javax.inject.Singleton
6 |
7 | @Singleton
8 | class AppInitializers @Inject constructor(
9 | private val initializers: Set<@JvmSuppressWildcards AppInitializer>
10 | ) {
11 | fun init(application: Application) {
12 | initializers.forEach { initializer ->
13 | initializer.init(application)
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/ShareDetails.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | data class ShareDetails(
4 | val title: String,
5 | val imdbId: ExternalId.Imdb
6 | ) {
7 | fun asMessage(): String {
8 | val contentLink = when (imdbId.type) {
9 | ExternalContentType.Movie, ExternalContentType.Tv -> "title"
10 | else -> "name"
11 | }
12 | val url = "https://www.imdb.com/$contentLink/${imdbId.id}"
13 |
14 | return "$title\n\n$url"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/ClearRecentlyBrowsedTvShowsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.repository.browsed.RecentlyBrowsedRepository
4 | import javax.inject.Inject
5 |
6 | class ClearRecentlyBrowsedTvShowsUseCaseImpl @Inject constructor(
7 | private val recentlyBrowsedRepository: RecentlyBrowsedRepository
8 | ) {
9 | operator fun invoke() {
10 | return recentlyBrowsedRepository.clearRecentlyBrowsedTvShows()
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/di/HelperBinds.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.di
2 |
3 | import com.example.movplayv3.utils.TextRecognitionHelper
4 | import com.example.movplayv3.utils.TextRecognitionHelperImpl
5 | import dagger.Binds
6 | import dagger.Module
7 | import dagger.hilt.InstallIn
8 | import dagger.hilt.components.SingletonComponent
9 |
10 | @Module
11 | @InstallIn(SingletonComponent::class)
12 | interface HelperBinds {
13 | @Binds
14 | fun provideTextRecognitionHelper(impl: TextRecognitionHelperImpl): TextRecognitionHelper
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetFavoriteMoviesIdsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.repository.favorites.FavoritesRepository
4 | import kotlinx.coroutines.flow.Flow
5 | import javax.inject.Inject
6 |
7 | class GetFavoriteMoviesIdsUseCaseImpl @Inject constructor(
8 | private val favoritesRepository: FavoritesRepository
9 | ) {
10 | operator fun invoke(): Flow> {
11 | return favoritesRepository.getFavoriteMoviesIds()
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/LikeMovieUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.model.movie.MovieDetails
4 | import com.example.movplayv3.data.repository.favorites.FavoritesRepository
5 | import javax.inject.Inject
6 |
7 | class LikeMovieUseCaseImpl @Inject constructor(
8 | private val favoritesRepository: FavoritesRepository
9 | ) {
10 | operator fun invoke(details: MovieDetails) {
11 | return favoritesRepository.likeMovie(details)
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/MediaAddSearchQueryUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import com.example.movplayv3.data.model.SearchQuery
4 | import com.example.movplayv3.data.repository.search.SearchRepository
5 | import javax.inject.Inject
6 |
7 | class MediaAddSearchQueryUseCaseImpl @Inject constructor(
8 | private val searchRepository: SearchRepository
9 | ) {
10 | operator fun invoke(searchQuery: SearchQuery) {
11 | return searchRepository.addSearchQuery(searchQuery)
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetFavoritesMovieCountUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.repository.favorites.FavoritesRepository
4 | import kotlinx.coroutines.flow.Flow
5 | import javax.inject.Inject
6 |
7 |
8 | class GetFavoritesMovieCountUseCaseImpl @Inject constructor(
9 | private val favoritesRepository: FavoritesRepository
10 | ) {
11 | operator fun invoke(): Flow {
12 | return favoritesRepository.getFavoriteMoviesCount()
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetFavoriteTvShowsCountUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.repository.favorites.FavoritesRepository
4 | import kotlinx.coroutines.flow.Flow
5 | import javax.inject.Inject
6 |
7 | class GetFavoriteTvShowsCountUseCaseImpl @Inject constructor(
8 | private val favoritesRepository: FavoritesRepository
9 | ) {
10 | operator fun invoke(): Flow {
11 | return favoritesRepository.getFavoriteTvShowsCount()
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Collection.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class Collection(
8 | val id: Int,
9 | val name: String?,
10 | @Json(name = "poster_path")
11 | val posterPath: String?,
12 | @Json(name = "backdrop_path")
13 | val backdropPath: String?,
14 | @Transient
15 | val posterUrl: String? = null,
16 | @Transient
17 | val backdropUrl: String? = null
18 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Image.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class Image(
8 | @Json(name = "aspect_ratio")
9 | val aspectRatio: Float,
10 | @Json(name = "file_path")
11 | val filePath: String,
12 | val height: Int,
13 | val width: Int,
14 | @Json(name = "vote_average")
15 | val voteAverage: Float,
16 | @Json(name = "vote_count")
17 | val voteCount: Int
18 | )
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/UnlikeMovieUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.model.movie.MovieDetails
4 | import com.example.movplayv3.data.repository.favorites.FavoritesRepository
5 | import javax.inject.Inject
6 |
7 | class UnlikeMovieUseCaseImpl @Inject constructor(
8 | private val favoritesRepository: FavoritesRepository
9 | ) {
10 | operator fun invoke(details: MovieDetails) {
11 | return favoritesRepository.unlikeMovie(details)
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetFavoriteTvShowIdsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.repository.favorites.FavoritesRepository
4 | import kotlinx.coroutines.flow.Flow
5 | import javax.inject.Inject
6 |
7 |
8 | class GetFavoriteTvShowIdsUseCaseImpl @Inject constructor(
9 | private val favoritesRepository: FavoritesRepository
10 | ) {
11 | operator fun invoke(): Flow> {
12 | return favoritesRepository.getFavoriteTvShowsIds()
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/LikeTvShowUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.model.tvshow.TvShowDetails
4 | import com.example.movplayv3.data.repository.favorites.FavoritesRepository
5 | import javax.inject.Inject
6 |
7 | class LikeTvShowUseCaseImpl @Inject constructor(
8 | private val favoritesRepository: FavoritesRepository
9 | ) {
10 | operator fun invoke(details: TvShowDetails) {
11 | return favoritesRepository.likeTvShow(details)
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/UnlikeTvShowUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.model.tvshow.TvShowDetails
4 | import com.example.movplayv3.data.repository.favorites.FavoritesRepository
5 | import javax.inject.Inject
6 |
7 | class UnlikeTvShowUseCaseImpl @Inject constructor(
8 | private val favoritesRepository: FavoritesRepository
9 | ) {
10 | operator fun invoke(details: TvShowDetails) {
11 | return favoritesRepository.unlikeTvShows(details)
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetMovieGenresUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.model.Genre
4 | import com.example.movplayv3.data.repository.config.ConfigRepository
5 | import kotlinx.coroutines.flow.Flow
6 | import javax.inject.Inject
7 |
8 |
9 | class GetMovieGenresUseCaseImpl @Inject constructor(
10 | private val configRepository: ConfigRepository
11 | ) {
12 | operator fun invoke(): Flow> {
13 | return configRepository.getMoviesGenres()
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetTvShowGenresUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.model.Genre
4 | import com.example.movplayv3.data.repository.config.ConfigRepository
5 | import kotlinx.coroutines.flow.Flow
6 | import javax.inject.Inject
7 |
8 |
9 | class GetTvShowGenresUseCaseImpl @Inject constructor(
10 | private val configRepository: ConfigRepository
11 | ) {
12 | operator fun invoke(): Flow> {
13 | return configRepository.getTvShowGenres()
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/GetDeviceLanguageUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import com.example.movplayv3.data.model.DeviceLanguage
4 | import com.example.movplayv3.data.repository.config.ConfigRepository
5 | import kotlinx.coroutines.flow.Flow
6 | import javax.inject.Inject
7 |
8 |
9 | class GetDeviceLanguageUseCaseImpl @Inject constructor(
10 | private val configRepository: ConfigRepository
11 | ) {
12 | operator fun invoke(): Flow {
13 | return configRepository.getDeviceLanguage()
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Creator.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class Creator(
8 | override val id: Int,
9 | @Json(name = "credit_id")
10 | val creditId: String,
11 | val name: String,
12 | val gender: Int?,
13 | @Json(name = "profile_path")
14 | override val profilePath: String?
15 | ) : Member {
16 | override val firstLine: String = name
17 | override val secondLine: String? = null
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Review.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 | import java.util.*
6 |
7 | @JsonClass(generateAdapter = true)
8 | data class Review(
9 | val id: String,
10 | val author: String,
11 | @Json(name = "author_details")
12 | val authorDetails: AuthorDetails,
13 | val content: String,
14 | @Json(name = "created_at")
15 | val createdAt: Date?,
16 | @Json(name = "updated_at")
17 | val updatedAt: Date?,
18 | val url: String
19 | )
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
12 |
13 | val DarkGreen = Color(0xFF0f9246)
14 | val LightGreen = Color(0xFF7dbb42)
15 | val Yellow = Color(0xFFfecc09)
16 | val Orange = Color(0xFFf68e1f)
17 | val Red = Color(0xFFef4723)
18 | val DarkRed = Color(0xFFbc2026)
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/chips/MovplayGenreChip.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.chips
2 |
3 | import androidx.compose.material3.*
4 | import androidx.compose.runtime.Composable
5 |
6 | @OptIn(ExperimentalMaterial3Api::class)
7 | @Composable
8 | fun MovplayGenreChip(
9 | text: String,
10 | ) {
11 | SuggestionChip(
12 | onClick = {},
13 | label = { Text(text) },
14 | colors = SuggestionChipDefaults.suggestionChipColors(
15 | containerColor = MaterialTheme.colorScheme.secondaryContainer),
16 | border = null
17 | )
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/AddRecentlyBrowsedMovieUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.model.movie.MovieDetails
4 | import com.example.movplayv3.data.repository.browsed.RecentlyBrowsedRepository
5 | import javax.inject.Inject
6 |
7 | class AddRecentlyBrowsedMovieUseCaseImpl @Inject constructor(
8 | private val recentlyBrowsedRepository: RecentlyBrowsedRepository
9 | ) {
10 | operator fun invoke(details: MovieDetails) {
11 | return recentlyBrowsedRepository.addRecentlyBrowsedMovie(details)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/texts/MovplayInfoText.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.texts
2 |
3 | import androidx.compose.material3.Text
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.graphics.Color
7 | import androidx.compose.ui.unit.sp
8 |
9 | @Composable
10 | fun MovplayInfoText(
11 | text: String,
12 | modifier: Modifier = Modifier
13 | ) {
14 | Text(
15 | modifier = modifier,
16 | text = text,
17 | // fontSize = 12.sp,
18 | // color = Color.White.copy(0.5f)
19 | )
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/RecentlyBrowsedMovie.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import com.example.movplayv3.data.model.Presentable
7 | import java.util.*
8 |
9 | @Entity
10 | data class RecentlyBrowsedMovie(
11 | @PrimaryKey
12 | override val id: Int,
13 | @ColumnInfo(name = "poster_path")
14 | override val posterPath: String?,
15 | override val title: String,
16 | @ColumnInfo(name = "added_date")
17 | val addedDate: Date
18 | ) : Presentable
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetAllMoviesWatchProvidersUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.model.ProviderSource
4 | import com.example.movplayv3.data.repository.config.ConfigRepository
5 | import kotlinx.coroutines.flow.Flow
6 | import javax.inject.Inject
7 |
8 | class GetAllMoviesWatchProvidersUseCaseImpl @Inject constructor(
9 | private val configRepository: ConfigRepository
10 | ) {
11 | operator fun invoke(): Flow> {
12 | return configRepository.getAllMoviesWatchProviders()
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/AddRecentlyBrowsedTvShowUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.model.tvshow.TvShowDetails
4 | import com.example.movplayv3.data.repository.browsed.RecentlyBrowsedRepository
5 | import javax.inject.Inject
6 |
7 | class AddRecentlyBrowsedTvShowUseCaseImpl @Inject constructor(
8 | private val recentlyBrowsedRepository: RecentlyBrowsedRepository
9 | ) {
10 | operator fun invoke(details: TvShowDetails) {
11 | return recentlyBrowsedRepository.addRecentlyBrowsedTvShows(details)
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetNextEpisodeDaysRemainingUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import java.util.*
4 | import java.util.concurrent.TimeUnit
5 | import javax.inject.Inject
6 |
7 |
8 | class GetNextEpisodeDaysRemainingUseCaseImpl @Inject constructor() {
9 | operator fun invoke(nextEpisodeAirDate: Date): Long {
10 | val millionSeconds = nextEpisodeAirDate.time - Calendar.getInstance().timeInMillis
11 | val daysDiff = TimeUnit.MILLISECONDS.toDays(millionSeconds)
12 | return if (daysDiff < 0) 0 else daysDiff
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetAllTvShowsWatchProvidersUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.model.ProviderSource
4 | import com.example.movplayv3.data.repository.config.ConfigRepository
5 | import kotlinx.coroutines.flow.Flow
6 | import javax.inject.Inject
7 |
8 |
9 | class GetAllTvShowsWatchProvidersUseCaseImpl @Inject constructor(
10 | private val configRepository: ConfigRepository
11 | ) {
12 | operator fun invoke(): Flow> {
13 | return configRepository.getAllTvShowWatchProviders()
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/CollectionResponse.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class CollectionResponse(
8 | val id: Int,
9 | val name: String,
10 | val overview: String?,
11 | val parts: List,
12 | @Json(name = "backdrop_path")
13 | val backdropPath: String?,
14 | @Json(name = "poster_path")
15 | val posterPath: String?,
16 | @Transient
17 | val posterUrl: String? = null,
18 | @Transient
19 | val backdropUrl: String? = null
20 | )
--------------------------------------------------------------------------------
/.idea/deploymentTargetDropDown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_facebook.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/SortTypeParam.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | enum class SortTypeParam(val value: String) {
4 | PopularityAsc("popularity.asc"),
5 | PopularityDesc("popularity.desc"),
6 | ReleaseDateAsc("release_date.asc"),
7 | ReleaseDateDesc("release_date.desc"),
8 | VoteAverageAsc("vote_average.asc"),
9 | VoteAverageDesc("vote_average.desc"),
10 | OriginalTitleAsc("original_title.asc"),
11 | OriginalTitleDesc("original_title.desc"),
12 | VoteCountAsc("vote_count.asc"),
13 | VoteCountDesc("vote_count.desc");
14 |
15 | override fun toString() = value
16 |
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/local/db/SearchQueryDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.local.db
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import com.example.movplayv3.data.model.SearchQuery
8 |
9 | @Dao
10 | interface SearchQueryDao {
11 | @Query("SELECT `query` FROM SearchQuery WHERE `query` LIKE '%' || :query || '%' ORDER BY last_use_date DESC LIMIT 3")
12 | suspend fun searchQueries(query: String): List
13 |
14 | @Insert(onConflict = OnConflictStrategy.REPLACE)
15 | suspend fun addQuery(searchQuery: SearchQuery)
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetFavoritesMoviesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import androidx.paging.PagingData
4 | import com.example.movplayv3.data.model.movie.MovieFavorite
5 | import com.example.movplayv3.data.repository.favorites.FavoritesRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetFavoritesMoviesUseCaseImpl @Inject constructor(
10 | private val favoritesRepository: FavoritesRepository
11 | ) {
12 | operator fun invoke(): Flow> {
13 | return favoritesRepository.favoriteMovies()
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_youtube.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/tvshow/TvShowFavorite.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.tvshow
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import com.example.movplayv3.data.model.Presentable
7 | import java.util.*
8 |
9 | @Entity
10 | data class TvShowFavorite(
11 | @PrimaryKey
12 | override val id: Int,
13 | @ColumnInfo(name = "poster_path")
14 | override val posterPath: String?,
15 | val name: String,
16 | @ColumnInfo(name = "added_date")
17 | val addedDate: Date
18 | ) : Presentable {
19 | @Transient
20 | override val title: String = name
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetFavoritesTvShowsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import androidx.paging.PagingData
4 | import com.example.movplayv3.data.model.tvshow.TvShowFavorite
5 | import com.example.movplayv3.data.repository.favorites.FavoritesRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 |
10 | class GetFavoritesTvShowsUseCaseImpl @Inject constructor(
11 | private val favoritesRepository: FavoritesRepository
12 | ) {
13 | operator fun invoke(): Flow> {
14 | return favoritesRepository.favoriteTvShows()
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Season.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class Season(
8 | override val id: Int,
9 | @Json(name = "air_date")
10 | val airDate: String?,
11 | val name: String,
12 | val overview: String,
13 | @Json(name = "episode_count")
14 | val episodeCount: Int,
15 | @Json(name = "season_number")
16 | val seasonNumber: Int,
17 | @Json(name = "poster_path")
18 | override val posterPath: String?
19 | ) : Presentable {
20 | override val title: String = name
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/MovieFavorite.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import com.example.movplayv3.data.model.Presentable
7 | import java.util.*
8 |
9 | @Entity
10 | data class MovieFavorite(
11 | @PrimaryKey
12 | override val id: Int,
13 | @ColumnInfo(name = "poster_path")
14 | override val posterPath: String?,
15 | override val title: String,
16 | @ColumnInfo(name = "original_title")
17 | val originalTitle: String,
18 | @ColumnInfo(name = "added_date")
19 | val addedDate: Date
20 | ) : Presentable
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/tvshow/RecentlyBrowsedTvShow.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.tvshow
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import com.example.movplayv3.data.model.Presentable
7 | import java.util.*
8 |
9 | @Entity
10 | data class RecentlyBrowsedTvShow(
11 | @PrimaryKey
12 | override val id: Int,
13 |
14 | @ColumnInfo(name = "poster_path")
15 | override val posterPath: String?,
16 |
17 | val name: String,
18 |
19 | @ColumnInfo(name = "added_date")
20 | val addedDate: Date
21 | ) : Presentable {
22 | @Transient
23 | override val title: String = name
24 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_instagram.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetRecentlyBrowsedMoviesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import androidx.paging.PagingData
4 | import com.example.movplayv3.data.model.movie.RecentlyBrowsedMovie
5 | import com.example.movplayv3.data.repository.browsed.RecentlyBrowsedRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetRecentlyBrowsedMoviesUseCaseImpl @Inject constructor(
10 | private val recentlyBrowsedRepository: RecentlyBrowsedRepository,
11 | ) {
12 | operator fun invoke(): Flow> {
13 | return recentlyBrowsedRepository.recentlyBrowsedMovies()
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetRecentlyBrowsedTvShowsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import androidx.paging.PagingData
4 | import com.example.movplayv3.data.model.tvshow.RecentlyBrowsedTvShow
5 | import com.example.movplayv3.data.repository.browsed.RecentlyBrowsedRepository
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class GetRecentlyBrowsedTvShowsUseCaseImpl @Inject constructor(
10 | private val recentlyBrowsedRepository: RecentlyBrowsedRepository
11 | ) {
12 | operator fun invoke(): Flow> {
13 | return recentlyBrowsedRepository.recentlyBrowsedTvShows()
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/ScanBitmapForTextUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import android.graphics.Bitmap
4 | import com.example.movplayv3.utils.Roi
5 | import com.example.movplayv3.utils.TextRecognitionHelper
6 | import kotlinx.coroutines.flow.Flow
7 | import javax.inject.Inject
8 |
9 | class ScanBitmapForTextUseCaseImpl @Inject constructor(
10 | private val textRecognitionHelper: TextRecognitionHelper
11 | ) {
12 | operator fun invoke(
13 | bitmap: Bitmap,
14 | rotation: Float,
15 | roi: Roi?
16 | ): Flow {
17 | return textRecognitionHelper.scanTextFromBitmap(bitmap, rotation, roi)
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/scanner/ScannerScreenUIState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.scanner
2 |
3 | import androidx.compose.runtime.Stable
4 |
5 | @Stable
6 | data class ScannerScreenUIState(
7 | val scanningInProgress: Boolean,
8 | val scanResult: ScanResult?,
9 | val validationErrorResId: Int?
10 | ) {
11 | companion object {
12 | val default: ScannerScreenUIState = ScannerScreenUIState(
13 | scanningInProgress = false,
14 | scanResult = null,
15 | validationErrorResId = 0
16 | )
17 | }
18 | }
19 |
20 | @Stable
21 | sealed class ScanResult {
22 | data class Success(val text: String) : ScanResult()
23 | data class Error(val message: String) : ScanResult()
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/ImagesConfig.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class ImagesConfig(
8 | @Json(name = "base_url")
9 | val baseUrl: String,
10 | @Json(name = "secure_base_url")
11 | val secureBaseUrl: String,
12 | @Json(name = "backdrop_sizes")
13 | val backdropSizes: List,
14 | @Json(name = "logo_sizes")
15 | val logoSizes: List,
16 | @Json(name = "poster_sizes")
17 | val posterSizes: List,
18 | @Json(name = "profile_sizes")
19 | val profileSizes: List,
20 | @Json(name = "still_sizes")
21 | val stillSizes: List
22 | )
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/reviews/ReviewsScreenUiState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.reviews
2 |
3 | import androidx.compose.runtime.Stable
4 | import androidx.paging.PagingData
5 | import com.example.movplayv3.data.model.Review
6 | import com.example.movplayv3.ui.screens.destinations.MovieScreenDestination
7 | import kotlinx.coroutines.flow.Flow
8 | import kotlinx.coroutines.flow.emptyFlow
9 |
10 | @Stable
11 | data class ReviewsScreenUiState(
12 | val startRoute: String,
13 | val reviews: Flow>
14 | ) {
15 | companion object {
16 | val default: ReviewsScreenUiState = ReviewsScreenUiState(
17 | startRoute = MovieScreenDestination.route,
18 | reviews = emptyFlow()
19 | )
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/tvshow/TvSeasonsResponse.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.tvshow
2 |
3 | import com.example.movplayv3.data.model.Episode
4 | import com.example.movplayv3.data.model.Presentable
5 | import com.squareup.moshi.Json
6 | import com.squareup.moshi.JsonClass
7 |
8 | @JsonClass(generateAdapter = true)
9 | data class TvSeasonsResponse(
10 | override val id: Int,
11 | @Json(name = "air_date")
12 | val airDate: String?,
13 | val name: String,
14 | val overview: String,
15 | @Json(name = "poster_path")
16 | override val posterPath: String?,
17 | @Json(name = "season_number")
18 | val seasonNumber: Int,
19 | val episodes: List
20 | ) : Presentable {
21 | override val title: String = name
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/texts/MovplaySectionLabel.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.texts
2 |
3 | import androidx.compose.material3.MaterialTheme
4 | import androidx.compose.material3.Text
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.text.font.FontWeight
8 | import androidx.compose.ui.text.style.TextOverflow
9 |
10 | @Composable
11 | fun MovplaySectionLabel(
12 | text: String,
13 | modifier: Modifier = Modifier
14 | ) {
15 | Text(
16 | modifier = modifier,
17 | text = text,
18 | style = MaterialTheme.typography.titleLarge,
19 | fontWeight = FontWeight.Bold,
20 | maxLines = 1,
21 | overflow = TextOverflow.Ellipsis
22 | )
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/theme/Spacing.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.theme
2 |
3 | import androidx.compose.material3.MaterialTheme
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.ReadOnlyComposable
6 | import androidx.compose.runtime.compositionLocalOf
7 | import androidx.compose.ui.unit.Dp
8 | import androidx.compose.ui.unit.dp
9 |
10 | data class Spacing(
11 | val default: Dp = 0.dp,
12 | val extraSmall: Dp = 4.dp,
13 | val small: Dp = 8.dp,
14 | val medium: Dp = 16.dp,
15 | val large: Dp = 32.dp,
16 | val extraLarge: Dp = 64.dp
17 | )
18 |
19 | val LocalSpacing = compositionLocalOf { Spacing() }
20 |
21 | val MaterialTheme.spacing: Spacing
22 | @Composable
23 | @ReadOnlyComposable
24 | get() = LocalSpacing.current
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/SearchResult.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class SearchResult(
8 | override val id: Int,
9 | @Json(name = "name")
10 | val tvShowName: String?,
11 | @Json(name = "title")
12 | val movieTitle: String?,
13 | @Json(name = "media_type")
14 | val mediaType: MediaType,
15 | val overview: String?,
16 | @Json(name = "poster_path")
17 | override val posterPath: String?
18 | ) : Presentable {
19 | override val title: String = when {
20 | !movieTitle.isNullOrEmpty() -> movieTitle
21 | !tvShowName.isNullOrEmpty() -> tvShowName
22 | else -> ""
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/favorite/FavoritesScreenUIState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.favorite
2 |
3 | import androidx.compose.runtime.Stable
4 | import androidx.paging.PagingData
5 | import com.example.movplayv3.data.model.FavoriteType
6 | import com.example.movplayv3.data.model.Presentable
7 | import kotlinx.coroutines.flow.Flow
8 | import kotlinx.coroutines.flow.emptyFlow
9 |
10 | @Stable
11 | data class FavoritesScreenUIState(
12 | val selectedFavouriteType: FavoriteType,
13 | val favorites: Flow>,
14 | ) {
15 | companion object {
16 | val default: FavoritesScreenUIState = FavoritesScreenUIState(
17 | selectedFavouriteType = FavoriteType.Movie,
18 | favorites = emptyFlow()
19 | )
20 | }
21 | }
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/GetMediaMultiSearchUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import androidx.paging.PagingData
4 | import com.example.movplayv3.data.model.DeviceLanguage
5 | import com.example.movplayv3.data.model.SearchResult
6 | import com.example.movplayv3.data.repository.search.SearchRepository
7 | import kotlinx.coroutines.flow.Flow
8 | import javax.inject.Inject
9 |
10 | class GetMediaMultiSearchUseCaseImpl @Inject constructor(
11 | private val searchRepository: SearchRepository
12 | ) {
13 | operator fun invoke(
14 | query: String,
15 | deviceLanguage: DeviceLanguage
16 | ): Flow> {
17 | return searchRepository.multiSearch(query = query, deviceLanguage = deviceLanguage)
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/button/MovplayFilterFloatingButton.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.button
2 |
3 | import androidx.compose.material.icons.Icons
4 | import androidx.compose.material.icons.filled.Filter
5 | import androidx.compose.material3.FloatingActionButton
6 | import androidx.compose.material3.Icon
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 |
10 | @Composable
11 | fun MovplayFilterFloatingButton(
12 | modifier: Modifier = Modifier,
13 | onClick: () -> Unit = {}
14 | ) {
15 | FloatingActionButton(
16 | modifier = modifier,
17 | onClick = onClick
18 | ) {
19 | Icon(
20 | imageVector = Icons.Filled.Filter,
21 | contentDescription = "filter"
22 | )
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/movplayv3/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.example.movplayv3", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/repository/search/SearchRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.repository.search
2 |
3 | import androidx.paging.PagingData
4 | import com.example.movplayv3.data.model.DeviceLanguage
5 | import com.example.movplayv3.data.model.SearchQuery
6 | import com.example.movplayv3.data.model.SearchResult
7 | import kotlinx.coroutines.flow.Flow
8 |
9 | interface SearchRepository {
10 | fun multiSearch(
11 | query: String,
12 | includeAdult: Boolean = false,
13 | year: Int? = null,
14 | releaseYear: Int? = null,
15 | deviceLanguage: DeviceLanguage = DeviceLanguage.default
16 | ): Flow>
17 |
18 | suspend fun searchQueries(query: String): List
19 |
20 | fun addSearchQuery(searchQuery: SearchQuery)
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/local/db/movie/MoviesDetailsRemoteKeysDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.local.db.movie
2 |
3 | import androidx.room.*
4 | import com.example.movplayv3.data.model.movie.MovieDetailsRemoteKey
5 | import com.example.movplayv3.utils.MovieEntityTypeConverters
6 |
7 | @TypeConverters(MovieEntityTypeConverters::class)
8 | @Dao
9 | interface MoviesDetailsRemoteKeysDao {
10 | @Query("SELECT * FROM MovieDetailsRemoteKey WHERE language=:language LIMIT 1")
11 | suspend fun getRemoteKey(language: String): MovieDetailsRemoteKey?
12 |
13 | @Insert(onConflict = OnConflictStrategy.REPLACE)
14 | suspend fun insertKey(remoteKey: MovieDetailsRemoteKey)
15 |
16 | @Query("DELETE FROM MovieDetailsRemoteKey WHERE language=:language")
17 | suspend fun deleteKeys(language: String)
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/local/db/tvshow/TvShowsDetailsRemoteKeysDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.local.db.tvshow
2 |
3 | import androidx.room.*
4 | import com.example.movplayv3.data.model.tvshow.TvShowDetailsRemoteKey
5 | import com.example.movplayv3.utils.TvShowEntityTypeConverters
6 |
7 | @TypeConverters(TvShowEntityTypeConverters::class)
8 | @Dao
9 | interface TvShowsDetailsRemoteKeysDao {
10 | @Query("SELECT * FROM TvShowDetailsRemoteKey WHERE language=:language LIMIT 1")
11 | suspend fun getRemoteKey(language: String): TvShowDetailsRemoteKey?
12 |
13 | @Insert(onConflict = OnConflictStrategy.REPLACE)
14 | suspend fun insertKey(remoteKey: TvShowDetailsRemoteKey)
15 |
16 | @Query("DELETE FROM TvShowDetailsRemoteKey WHERE language=:language")
17 | suspend fun deleteKeys(language: String)
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/others/MovplayAnimatedContentContainer.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.others
2 |
3 | import androidx.compose.animation.*
4 | import androidx.compose.animation.core.tween
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 |
8 | @Composable
9 | fun MovplayAnimatedContentContainer(
10 | modifier: Modifier = Modifier,
11 | visible: Boolean,
12 | content: @Composable AnimatedVisibilityScope.() -> Unit = {}
13 | ) {
14 | AnimatedVisibility(
15 | modifier = modifier,
16 | enter = fadeIn(tween(300, delayMillis = 100)) + expandVertically(tween(300)),
17 | exit = fadeOut(tween(300)) + shrinkVertically(tween(300, delayMillis = 100)),
18 | visible = visible,
19 | content = content
20 | )
21 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/local/db/movie/MoviesDetailsDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.local.db.movie
2 |
3 | import androidx.paging.PagingSource
4 | import androidx.room.*
5 | import com.example.movplayv3.data.model.movie.MovieDetailEntity
6 | import com.example.movplayv3.utils.MovieEntityTypeConverters
7 |
8 | @TypeConverters(MovieEntityTypeConverters::class)
9 | @Dao
10 | interface MoviesDetailsDao {
11 | @Query("SELECT * FROM MovieDetailEntity WHERE language=:language")
12 | fun getAllMovies(language: String): PagingSource
13 |
14 | @Insert(onConflict = OnConflictStrategy.IGNORE)
15 | suspend fun addMovies(movies: List)
16 |
17 | @Query("DELETE FROM MovieDetailEntity WHERE language=:language")
18 | suspend fun deleteMovieDetails(language: String)
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/texts/MovplayAdditionalInfoText.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.texts
2 |
3 | import androidx.compose.material3.MaterialTheme
4 | import androidx.compose.material3.Text
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.text.font.FontWeight
9 | import androidx.compose.ui.unit.sp
10 |
11 | @Composable
12 | fun MovplayAdditionalInfoText(
13 | infoTexts: List,
14 | modifier: Modifier = Modifier
15 | ) {
16 | val text = infoTexts.joinToString(separator = " · ")
17 |
18 | Text(
19 | modifier = modifier,
20 | text = text,
21 | style = MaterialTheme.typography.titleMedium,
22 | fontWeight = FontWeight.Normal
23 | )
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/local/db/tvshow/TvShowsDetailsDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.local.db.tvshow
2 |
3 | import androidx.paging.PagingSource
4 | import androidx.room.*
5 | import com.example.movplayv3.data.model.tvshow.TvShowDetailEntity
6 | import com.example.movplayv3.utils.TvShowEntityTypeConverters
7 |
8 | @TypeConverters(TvShowEntityTypeConverters::class)
9 | @Dao
10 | interface TvShowsDetailsDao {
11 | @Query("SELECT * FROM TvShowDetailEntity WHERE language=:language")
12 | fun getAllTvShows(language: String): PagingSource
13 |
14 | @Insert(onConflict = OnConflictStrategy.IGNORE)
15 | suspend fun addTvShow(movies: List)
16 |
17 | @Query("DELETE FROM TvShowDetailEntity WHERE language=:language")
18 | suspend fun deleteAllTvShows(language: String)
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/CrewMember.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class CrewMember(
8 | override val id: Int,
9 | val adult: Boolean,
10 | val gender: Int?,
11 | @Json(name = "known_for_department")
12 | val knownForDepartment: String?,
13 | val name: String,
14 | @Json(name = "original_name")
15 | val originalName: String?,
16 | val popularity: Float,
17 | @Json(name = "profile_path")
18 | override val profilePath: String?,
19 | @Json(name = "credit_id")
20 | val creditId: String,
21 | val department: String,
22 | val job: String,
23 | ) : Member {
24 | override val firstLine: String = name
25 | override val secondLine: String = job
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/theme/Sizes.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.theme
2 |
3 | import androidx.compose.material3.MaterialTheme
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.ReadOnlyComposable
6 | import androidx.compose.runtime.compositionLocalOf
7 | import androidx.compose.ui.unit.Dp
8 | import androidx.compose.ui.unit.dp
9 |
10 | data class Sizes(
11 | val presentableItemSmall: Size = Size(120.dp, 180.dp),
12 | val presentableItemBig: Size = Size(160.dp, 240.dp),
13 | val videoItem: Size = Size(200.dp, 110.dp)
14 | )
15 |
16 | data class Size(val width: Dp, val height: Dp) {
17 | val ratio: Float = width / height
18 | }
19 |
20 | val LocalSizes = compositionLocalOf { Sizes() }
21 |
22 | val MaterialTheme.sizes: Sizes
23 | @Composable
24 | @ReadOnlyComposable
25 | get() = LocalSizes.current
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/MovplayV3Application.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3
2 |
3 | import android.app.Application
4 | import coil.ImageLoader
5 | import coil.ImageLoaderFactory
6 | import com.example.movplayv3.data.initializer.AppInitializers
7 | import dagger.hilt.android.HiltAndroidApp
8 | import timber.log.Timber
9 | import javax.inject.Inject
10 |
11 | @HiltAndroidApp
12 | class MovplayV3Application : Application(), ImageLoaderFactory {
13 |
14 | @Inject
15 | lateinit var initializers: AppInitializers
16 |
17 | @Inject
18 | lateinit var imageLoader: ImageLoader
19 |
20 | override fun onCreate() {
21 | super.onCreate()
22 |
23 | if (BuildConfig.DEBUG) {
24 | Timber.plant(Timber.DebugTree())
25 | }
26 | initializers.init(this)
27 | }
28 |
29 | override fun newImageLoader() = imageLoader
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/SeasonDetails.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 | import java.util.*
6 |
7 | @JsonClass(generateAdapter = true)
8 | data class SeasonDetails(
9 | @Json(name = "air_date")
10 | val airDate: Date?,
11 | val episodes: List,
12 | val name: String,
13 | override val overview: String,
14 | override val id: Int,
15 | @Json(name = "season_number")
16 | val seasonNumber: Int,
17 | @Json(name = "poster_path")
18 | override val posterPath: String?
19 | ) : DetailPresentable {
20 | override val adult: Boolean? = null
21 | override val backdropPath: String? = null
22 | override val voteAverage: Float = 0f
23 | override val voteCount: Int = 0
24 | override val title: String = name
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/ExternalId.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import androidx.annotation.DrawableRes
4 | import com.example.movplayv3.R
5 |
6 | sealed class ExternalId(@DrawableRes val drawableRes: Int) {
7 | data class Imdb(val id: String, val type: ExternalContentType) :
8 | ExternalId(drawableRes = R.drawable.ic_imdb)
9 |
10 | data class Facebook(val id: String, val type: ExternalContentType) :
11 | ExternalId(drawableRes = R.drawable.ic_facebook)
12 |
13 | data class Instagram(val id: String, val type: ExternalContentType) :
14 | ExternalId(drawableRes = R.drawable.ic_instagram)
15 |
16 | data class Twitter(val id: String, val type: ExternalContentType) :
17 | ExternalId(drawableRes = R.drawable.ic_twitter)
18 | }
19 |
20 | enum class ExternalContentType {
21 | Movie, Tv, Person;
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/MovieEntity.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.Index
6 | import androidx.room.PrimaryKey
7 | import com.example.movplayv3.data.model.Presentable
8 |
9 | @Entity(indices = [Index(value = ["type", "language"])])
10 | data class MovieEntity(
11 | override val id: Int,
12 | val type: MovieEntityType,
13 | @ColumnInfo(name = "poster_path")
14 | override val posterPath: String?,
15 | override val title: String,
16 | @ColumnInfo(name = "original_title")
17 | val originalTitle: String,
18 | val language: String
19 | ) : Presentable {
20 | @PrimaryKey(autoGenerate = true)
21 | var entityId: Int = 0
22 | }
23 |
24 | enum class MovieEntityType {
25 | Discover, Upcoming, Trending, TopRated, Popular
26 | }
27 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/tvshow/TvShowEntity.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.tvshow
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.Index
6 | import androidx.room.PrimaryKey
7 | import com.example.movplayv3.data.model.Presentable
8 |
9 | @Entity(indices = [Index(value = ["type", "language"])])
10 | data class TvShowEntity(
11 | override val id: Int,
12 | val type: TvShowEntityType,
13 | @ColumnInfo(name = "poster_path")
14 | override val posterPath: String?,
15 | override val title: String,
16 | @ColumnInfo(name = "original_name")
17 | val originalName: String?,
18 | val language: String
19 | ) : Presentable {
20 | @PrimaryKey(autoGenerate = true)
21 | var entityId: Int = 0
22 | }
23 |
24 | enum class TvShowEntityType {
25 | Trending, TopRated, AiringToday, Popular, Discover
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/GetPersonExternalIdsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import com.example.movplayv3.data.model.DeviceLanguage
4 | import com.example.movplayv3.data.model.ExternalIds
5 | import com.example.movplayv3.data.remote.api.ApiResponse
6 | import com.example.movplayv3.data.remote.api.awaitApiResponse
7 | import com.example.movplayv3.data.repository.person.PersonRepository
8 | import javax.inject.Inject
9 |
10 | class GetPersonExternalIdsUseCaseImpl @Inject constructor(
11 | private val personRepository: PersonRepository
12 | ) {
13 | suspend operator fun invoke(
14 | personId: Int,
15 | deviceLanguage: DeviceLanguage
16 | ): ApiResponse {
17 | return personRepository.getExternalIds(personId = personId, deviceLanguage = deviceLanguage)
18 | .awaitApiResponse()
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/repository/person/PersonRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.repository.person
2 |
3 | import com.example.movplayv3.data.model.CombinedCredits
4 | import com.example.movplayv3.data.model.DeviceLanguage
5 | import com.example.movplayv3.data.model.ExternalIds
6 | import com.example.movplayv3.data.model.PersonDetails
7 | import retrofit2.Call
8 |
9 | interface PersonRepository {
10 | fun getPersonDetails(
11 | personId: Int,
12 | deviceLanguage: DeviceLanguage = DeviceLanguage.default
13 | ) : Call
14 |
15 | fun getCombinedCredits(
16 | personId: Int,
17 | deviceLanguage: DeviceLanguage = DeviceLanguage.default
18 | ) : Call
19 |
20 | fun getExternalIds(
21 | personId: Int,
22 | deviceLanguage: DeviceLanguage = DeviceLanguage.default
23 | ): Call
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/utils/NavigationUtils.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.utils
2 |
3 | import androidx.navigation.NavController
4 | import com.example.movplayv3.ui.screens.destinations.MovieScreenDestination
5 |
6 | fun NavController.safeNavigate(
7 | route: String,
8 | onSameRouteSelected: (String) -> Unit = {}
9 | ) {
10 | val currentRoute = currentBackStackEntry?.destination?.route
11 |
12 | if (currentRoute == route) {
13 | onSameRouteSelected(route)
14 | return
15 | }
16 |
17 | val isInBackstack = backQueue.map { entry -> entry.destination.route }
18 | .any { it == route }
19 |
20 | if (isInBackstack) {
21 | popBackStack(
22 | route = route,
23 | inclusive = false
24 | )
25 | } else {
26 | navigate(route) {
27 | popUpTo(MovieScreenDestination.route)
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/di/AppModuleBinds.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.di
2 |
3 | import com.example.movplayv3.data.initializer.AppInitializer
4 | import com.example.movplayv3.data.initializer.ConfigDataSourceInitializer
5 | import com.example.movplayv3.data.initializer.FirebaseInitializer
6 | import dagger.Binds
7 | import dagger.Module
8 | import dagger.hilt.InstallIn
9 | import dagger.hilt.components.SingletonComponent
10 | import dagger.multibindings.IntoSet
11 |
12 | @Module
13 | @InstallIn(SingletonComponent::class)
14 | abstract class AppModuleBinds {
15 |
16 | @Binds
17 | @IntoSet
18 | abstract fun provideFirebaseInitializer(
19 | initializer: FirebaseInitializer
20 | ): AppInitializer
21 |
22 | @Binds
23 | @IntoSet
24 | abstract fun provideConfigRepositoryInitializer(
25 | initializer: ConfigDataSourceInitializer
26 | ): AppInitializer
27 |
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/utils/Formatter.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.utils
2 |
3 | import java.text.NumberFormat
4 | import java.util.*
5 | import kotlin.time.Duration.Companion.minutes
6 |
7 | fun Int.formattedRuntime(): String? {
8 | return minutes.toComponents { hours, minutes, _, _ ->
9 | val hoursString = if (hours > 0) "${hours}h" else null
10 | val minutesString = if (minutes > 0) "${minutes}m" else null
11 |
12 | listOfNotNull(hoursString, minutesString).run {
13 | if (isEmpty()) null else joinToString(separator = " ")
14 | }
15 | }
16 | }
17 |
18 | fun Float.singleDecimalPlaceFormatted(): String {
19 | return String.format("%.1f", this)
20 | }
21 |
22 | fun Long.formattedMoney(): String {
23 | return NumberFormat.getCurrencyInstance(Locale.US).apply {
24 | maximumFractionDigits = 0
25 | }.format(this).replace(",", " ")
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/CastMember.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class CastMember(
8 | override val id: Int,
9 | val adult: Boolean,
10 | val gender: Int?,
11 | @Json(name = "known_for_department")
12 | val knownForDepartment: String?,
13 | val name: String,
14 | @Json(name = "original_name")
15 | val originalName: String?,
16 | val popularity: Float,
17 | @Json(name = "profile_path")
18 | override val profilePath: String?,
19 | @Json(name = "cast_id")
20 | val castId: Int,
21 | val character: String?,
22 | @Json(name = "credit_id")
23 | val creditId: String,
24 | val order: Int
25 | ) : Member {
26 | override val firstLine: String = name
27 | override val secondLine: String? = character
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetMovieCreditUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.model.Credits
4 | import com.example.movplayv3.data.model.DeviceLanguage
5 | import com.example.movplayv3.data.remote.api.ApiResponse
6 | import com.example.movplayv3.data.remote.api.awaitApiResponse
7 | import com.example.movplayv3.data.repository.movie.MovieRepository
8 | import javax.inject.Inject
9 |
10 | class GetMovieCreditUseCaseImpl @Inject constructor(
11 | private val movieRepository: MovieRepository
12 | ) {
13 | suspend operator fun invoke(
14 | movieId: Int,
15 | deviceLanguage: DeviceLanguage
16 | ): ApiResponse {
17 | return movieRepository.movieCredits(
18 | movieId = movieId,
19 | isoCode = deviceLanguage.languageCode
20 | ).awaitApiResponse()
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/sections/MovplayGenresSection.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.sections
2 |
3 | import androidx.compose.material3.MaterialTheme
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import com.example.movplayv3.data.model.Genre
7 | import com.example.movplayv3.ui.components.chips.MovplayGenreChip
8 | import com.example.movplayv3.ui.theme.spacing
9 | import com.google.accompanist.flowlayout.FlowRow
10 |
11 | @Composable
12 | fun MovplayGenresSection(
13 | genres: List,
14 | modifier: Modifier = Modifier
15 | ) {
16 | FlowRow(
17 | modifier = modifier,
18 | mainAxisSpacing = MaterialTheme.spacing.extraSmall,
19 | crossAxisSpacing = MaterialTheme.spacing.extraSmall
20 | ) {
21 | genres.map { genre ->
22 | MovplayGenreChip(text = genre.name)
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/PersonDetails.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 | import java.util.*
6 |
7 | @JsonClass(generateAdapter = true)
8 | data class PersonDetails(
9 | val id: Int,
10 | val birthday: Date?,
11 | @Json(name = "deathday")
12 | val deathDate: Date?,
13 | @Json(name = "known_for_department")
14 | val knownFor: String,
15 | @Json(name = "also_knows_as")
16 | val alsoKnowsAs: List?,
17 | val gender: Int,
18 | val name: String,
19 | val biography: String,
20 | val popularity: Float,
21 | @Json(name = "place_of_birth")
22 | val birthPlace: String?,
23 | @Json(name = "profile_path")
24 | val profilePath: String?,
25 | val adult: Boolean,
26 | val homepage: String?,
27 | @Json(name = "imdb_id")
28 | val imdbId: String?
29 | )
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/GetPersonDetailsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import com.example.movplayv3.data.model.DeviceLanguage
4 | import com.example.movplayv3.data.model.PersonDetails
5 | import com.example.movplayv3.data.remote.api.ApiResponse
6 | import com.example.movplayv3.data.remote.api.awaitApiResponse
7 | import com.example.movplayv3.data.repository.person.PersonRepository
8 | import javax.inject.Inject
9 |
10 | class GetPersonDetailsUseCaseImpl @Inject constructor(
11 | private val personRepository: PersonRepository
12 | ) {
13 | suspend operator fun invoke(
14 | personId: Int,
15 | deviceLanguage: DeviceLanguage
16 | ): ApiResponse {
17 | return personRepository.getPersonDetails(
18 | personId = personId,
19 | deviceLanguage = deviceLanguage
20 | ).awaitApiResponse()
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/local/db/movie/MoviesDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.local.db.movie
2 |
3 | import androidx.paging.PagingSource
4 | import androidx.room.*
5 | import com.example.movplayv3.data.model.movie.MovieEntity
6 | import com.example.movplayv3.data.model.movie.MovieEntityType
7 | import com.example.movplayv3.utils.MovieEntityTypeConverters
8 |
9 | @TypeConverters(MovieEntityTypeConverters::class)
10 | @Dao
11 | interface MoviesDao {
12 | @Query("SELECT * FROM MovieEntity WHERE type=:type AND language=:language")
13 | fun getAllMovies(type: MovieEntityType, language: String): PagingSource
14 |
15 | @Insert(onConflict = OnConflictStrategy.IGNORE)
16 | suspend fun addMovies(movies: List)
17 |
18 | @Query("DELETE FROM MovieEntity WHERE type=:type AND language=:language")
19 | suspend fun deleteMoviesOfType(type: MovieEntityType, language: String)
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/local/db/movie/MoviesRemoteKeysDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.local.db.movie
2 |
3 | import androidx.room.*
4 | import com.example.movplayv3.data.model.movie.MovieEntityType
5 | import com.example.movplayv3.data.model.movie.MoviesRemoteKeys
6 | import com.example.movplayv3.utils.MovieEntityTypeConverters
7 |
8 | @TypeConverters(MovieEntityTypeConverters::class)
9 | @Dao
10 | interface MoviesRemoteKeysDao {
11 | @Query("SELECT * FROM MoviesRemoteKeys WHERE type=:type AND language=:language")
12 | suspend fun getRemoteKey(type: MovieEntityType, language: String): MoviesRemoteKeys?
13 |
14 | @Insert(onConflict = OnConflictStrategy.REPLACE)
15 | suspend fun insertKey(remoteKey: MoviesRemoteKeys)
16 |
17 | @Query("DELETE FROM MoviesRemoteKeys WHERE type=:type AND language=:language")
18 | suspend fun deleteRemoteKeysOfType(type: MovieEntityType, language: String)
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/repository/browsed/RecentlyBrowsedRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.repository.browsed
2 |
3 | import androidx.paging.PagingData
4 | import com.example.movplayv3.data.model.movie.MovieDetails
5 | import com.example.movplayv3.data.model.movie.RecentlyBrowsedMovie
6 | import com.example.movplayv3.data.model.tvshow.RecentlyBrowsedTvShow
7 | import com.example.movplayv3.data.model.tvshow.TvShowDetails
8 | import kotlinx.coroutines.flow.Flow
9 |
10 | interface RecentlyBrowsedRepository {
11 | fun addRecentlyBrowsedMovie(movieDetails: MovieDetails)
12 |
13 | fun clearRecentlyBrowsedMovies()
14 |
15 | fun clearRecentlyBrowsedTvShows()
16 |
17 | fun recentlyBrowsedMovies(): Flow>
18 |
19 | fun addRecentlyBrowsedTvShows(tvShowDetails: TvShowDetails)
20 |
21 | fun recentlyBrowsedTvShows(): Flow>
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/GetCombinedCreditsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import com.example.movplayv3.data.model.CombinedCredits
4 | import com.example.movplayv3.data.model.DeviceLanguage
5 | import com.example.movplayv3.data.remote.api.ApiResponse
6 | import com.example.movplayv3.data.remote.api.awaitApiResponse
7 | import com.example.movplayv3.data.repository.person.PersonRepository
8 | import javax.inject.Inject
9 |
10 | class GetCombinedCreditsUseCaseImpl @Inject constructor(
11 | private val personRepository: PersonRepository
12 | ) {
13 | suspend operator fun invoke(
14 | personId: Int,
15 | deviceLanguage: DeviceLanguage
16 | ): ApiResponse {
17 | return personRepository.getCombinedCredits(
18 | personId = personId,
19 | deviceLanguage = deviceLanguage
20 | ).awaitApiResponse()
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetMovieDetailsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.model.DeviceLanguage
4 | import com.example.movplayv3.data.model.movie.MovieDetails
5 | import com.example.movplayv3.data.remote.api.ApiResponse
6 | import com.example.movplayv3.data.remote.api.awaitApiResponse
7 | import com.example.movplayv3.data.repository.movie.MovieRepository
8 | import javax.inject.Inject
9 |
10 | class GetMovieDetailsUseCaseImpl @Inject constructor(
11 | private val movieRepository: MovieRepository
12 | ) {
13 | suspend operator fun invoke(
14 | movieId: Int,
15 | deviceLanguage: DeviceLanguage
16 | ): ApiResponse {
17 | return movieRepository.movieDetails(
18 | movieId = movieId,
19 | isoCode = deviceLanguage.languageCode
20 | ).awaitApiResponse()
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetOtherDirectorMoviesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import androidx.paging.PagingData
4 | import com.example.movplayv3.data.model.CrewMember
5 | import com.example.movplayv3.data.model.DeviceLanguage
6 | import com.example.movplayv3.data.model.movie.Movie
7 | import com.example.movplayv3.data.repository.movie.MovieRepository
8 | import kotlinx.coroutines.flow.Flow
9 | import javax.inject.Inject
10 |
11 | class GetOtherDirectorMoviesUseCaseImpl @Inject constructor(
12 | private val movieRepository: MovieRepository
13 | ) {
14 | operator fun invoke(
15 | mainDirector: CrewMember,
16 | deviceLanguage: DeviceLanguage
17 | ): Flow> {
18 | return movieRepository.moviesOfDirector(
19 | directorId = mainDirector.id,
20 | deviceLanguage = deviceLanguage
21 | )
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/local/db/tvshow/TvShowsRemoteKeysDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.local.db.tvshow
2 |
3 | import androidx.room.*
4 | import com.example.movplayv3.data.model.tvshow.TvShowEntityType
5 | import com.example.movplayv3.data.model.tvshow.TvShowsRemoteKeys
6 | import com.example.movplayv3.utils.TvShowEntityTypeConverters
7 |
8 | @TypeConverters(TvShowEntityTypeConverters::class)
9 | @Dao
10 | interface TvShowsRemoteKeysDao {
11 | @Query("SELECT * FROM TvShowsRemoteKeys WHERE type=:type AND language=:language")
12 | suspend fun getRemoteKey(type: TvShowEntityType, language: String): TvShowsRemoteKeys?
13 |
14 | @Insert(onConflict = OnConflictStrategy.REPLACE)
15 | suspend fun insertKey(remoteKey: TvShowsRemoteKeys)
16 |
17 | @Query("DELETE FROM TvShowsRemoteKeys WHERE type=:type AND language=:language")
18 | suspend fun deleteRemoteKeysOfType(type: TvShowEntityType, language: String)
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/local/db/tvshow/TvShowsDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.local.db.tvshow
2 |
3 | import androidx.paging.PagingSource
4 | import androidx.room.*
5 | import com.example.movplayv3.data.model.tvshow.TvShowEntity
6 | import com.example.movplayv3.data.model.tvshow.TvShowEntityType
7 | import com.example.movplayv3.utils.TvShowEntityTypeConverters
8 |
9 | @TypeConverters(TvShowEntityTypeConverters::class)
10 | @Dao
11 | interface TvShowsDao {
12 | @Query("SELECT * FROM TvShowEntity WHERE type=:type AND language=:language")
13 | fun getAllTvShows(type: TvShowEntityType, language: String): PagingSource
14 |
15 | @Insert(onConflict = OnConflictStrategy.IGNORE)
16 | suspend fun addTvShow(movies: List)
17 |
18 | @Query("DELETE FROM TvShowEntity WHERE type=:type AND language=:language")
19 | suspend fun deleteTvShowsOfType(type: TvShowEntityType, language: String)
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetUpcomingMoviesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import androidx.paging.PagingData
4 | import androidx.paging.map
5 | import com.example.movplayv3.data.model.DeviceLanguage
6 | import com.example.movplayv3.data.model.Presentable
7 | import com.example.movplayv3.data.repository.movie.MovieRepository
8 | import kotlinx.coroutines.ExperimentalCoroutinesApi
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.mapLatest
11 | import javax.inject.Inject
12 |
13 | class GetUpcomingMoviesUseCaseImpl @Inject constructor(
14 | private val movieRepository: MovieRepository
15 | ) {
16 | @OptIn(ExperimentalCoroutinesApi::class)
17 | operator fun invoke(deviceLanguage: DeviceLanguage): Flow> {
18 | return movieRepository.upcomingMovies(deviceLanguage)
19 | .mapLatest { data -> data.map { it } }
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetTvShowDetailsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.model.DeviceLanguage
4 | import com.example.movplayv3.data.model.tvshow.TvShowDetails
5 | import com.example.movplayv3.data.remote.api.ApiResponse
6 | import com.example.movplayv3.data.remote.api.awaitApiResponse
7 | import com.example.movplayv3.data.repository.tvshow.TvShowRepository
8 | import javax.inject.Inject
9 |
10 |
11 | class GetTvShowDetailsUseCaseImpl @Inject constructor(
12 | private val tvShowRepository: TvShowRepository
13 | ) {
14 | suspend operator fun invoke(
15 | tvShowId: Int,
16 | deviceLanguage: DeviceLanguage
17 | ): ApiResponse {
18 | return tvShowRepository.getTvShowDetails(
19 | tvShowId = tvShowId,
20 | deviceLanguage = deviceLanguage
21 | ).awaitApiResponse()
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetDiscoverAllMoviesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import androidx.paging.PagingData
4 | import androidx.paging.map
5 | import com.example.movplayv3.data.model.DeviceLanguage
6 | import com.example.movplayv3.data.model.Presentable
7 | import com.example.movplayv3.data.repository.movie.MovieRepository
8 | import kotlinx.coroutines.ExperimentalCoroutinesApi
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.mapLatest
11 | import javax.inject.Inject
12 |
13 | class GetDiscoverAllMoviesUseCaseImpl @Inject constructor(
14 | private val movieRepository: MovieRepository
15 | ) {
16 | @OptIn(ExperimentalCoroutinesApi::class)
17 | operator fun invoke(deviceLanguage: DeviceLanguage): Flow> {
18 | return movieRepository.discoverMovies(deviceLanguage)
19 | .mapLatest { data -> data.map { it } }
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetTopRatedMoviesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import androidx.paging.PagingData
4 | import androidx.paging.map
5 | import com.example.movplayv3.data.model.DeviceLanguage
6 | import com.example.movplayv3.data.model.Presentable
7 | import com.example.movplayv3.data.repository.movie.MovieRepository
8 | import kotlinx.coroutines.ExperimentalCoroutinesApi
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.mapLatest
11 | import javax.inject.Inject
12 |
13 |
14 | class GetTopRatedMoviesUseCaseImpl @Inject constructor(
15 | private val movieRepository: MovieRepository
16 | ) {
17 | @OptIn(ExperimentalCoroutinesApi::class)
18 | operator fun invoke(deviceLanguage: DeviceLanguage): Flow> {
19 | return movieRepository.topRatedMovies(deviceLanguage)
20 | .mapLatest { data -> data.map { it } }
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetTrendingMoviesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import androidx.paging.PagingData
4 | import androidx.paging.map
5 | import com.example.movplayv3.data.model.DeviceLanguage
6 | import com.example.movplayv3.data.model.Presentable
7 | import com.example.movplayv3.data.repository.movie.MovieRepository
8 | import kotlinx.coroutines.ExperimentalCoroutinesApi
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.mapLatest
11 | import javax.inject.Inject
12 |
13 |
14 | class GetTrendingMoviesUseCaseImpl @Inject constructor(
15 | private val movieRepository: MovieRepository
16 | ) {
17 | @OptIn(ExperimentalCoroutinesApi::class)
18 | operator fun invoke(deviceLanguage: DeviceLanguage): Flow> {
19 | return movieRepository.trendingMovies(deviceLanguage)
20 | .mapLatest { data -> data.map { it } }
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/browse/movies/BrowseMoviesScreenUIState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.browse.movies
2 |
3 | import androidx.compose.runtime.Stable
4 | import androidx.paging.PagingData
5 | import com.example.movplayv3.data.model.Presentable
6 | import com.example.movplayv3.data.model.movie.MovieType
7 | import kotlinx.coroutines.flow.Flow
8 | import kotlinx.coroutines.flow.emptyFlow
9 |
10 | @Stable
11 | data class BrowseMoviesScreenUIState(
12 | val selectedMovieType: MovieType,
13 | val movies: Flow>,
14 | val favoriteMoviesCount: Int
15 | ) {
16 | companion object {
17 | fun getDefault(selectedMovieType: MovieType): BrowseMoviesScreenUIState {
18 | return BrowseMoviesScreenUIState(
19 | selectedMovieType = selectedMovieType,
20 | movies = emptyFlow(),
21 | favoriteMoviesCount = 0
22 | )
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetPopularMoviesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import androidx.paging.PagingData
4 | import androidx.paging.map
5 | import com.example.movplayv3.data.model.DeviceLanguage
6 | import com.example.movplayv3.data.model.Presentable
7 | import com.example.movplayv3.data.repository.movie.MovieRepository
8 | import kotlinx.coroutines.ExperimentalCoroutinesApi
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.mapLatest
11 | import javax.inject.Inject
12 |
13 |
14 | class GetPopularMoviesUseCaseImpl @Inject constructor(
15 | private val movieRepository: MovieRepository
16 | ) {
17 | @OptIn(ExperimentalCoroutinesApi::class)
18 | operator fun invoke(deviceLanguage: DeviceLanguage): Flow> {
19 | return movieRepository.popularMovies(
20 | deviceLanguage
21 | ).mapLatest { data -> data.map { it } }
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetTopRatedTvShowsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import androidx.paging.PagingData
4 | import androidx.paging.map
5 | import com.example.movplayv3.data.model.DeviceLanguage
6 | import com.example.movplayv3.data.model.Presentable
7 | import com.example.movplayv3.data.repository.tvshow.TvShowRepository
8 | import kotlinx.coroutines.ExperimentalCoroutinesApi
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.mapLatest
11 | import javax.inject.Inject
12 |
13 |
14 | class GetTopRatedTvShowsUseCaseImpl @Inject constructor(
15 | private val tvShowRepository: TvShowRepository
16 | ) {
17 | @OptIn(ExperimentalCoroutinesApi::class)
18 | operator fun invoke(deviceLanguage: DeviceLanguage): Flow> {
19 | return tvShowRepository.topRatedTvShows(deviceLanguage)
20 | .mapLatest { data -> data.map { it } }
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetTrendingTvShowsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import androidx.paging.PagingData
4 | import androidx.paging.map
5 | import com.example.movplayv3.data.model.DeviceLanguage
6 | import com.example.movplayv3.data.model.Presentable
7 | import com.example.movplayv3.data.repository.tvshow.TvShowRepository
8 | import kotlinx.coroutines.ExperimentalCoroutinesApi
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.mapLatest
11 | import javax.inject.Inject
12 |
13 |
14 | class GetTrendingTvShowsUseCaseImpl @Inject constructor(
15 | private val tvShowRepository: TvShowRepository
16 | ) {
17 | @OptIn(ExperimentalCoroutinesApi::class)
18 | operator fun invoke(deviceLanguage: DeviceLanguage): Flow> {
19 | return tvShowRepository.trendingTvShows(deviceLanguage)
20 | .mapLatest { data -> data.map { it } }
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_outline_no_photography_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetAiringTodayTvShowsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import androidx.paging.PagingData
4 | import androidx.paging.map
5 | import com.example.movplayv3.data.model.DeviceLanguage
6 | import com.example.movplayv3.data.model.Presentable
7 | import com.example.movplayv3.data.repository.tvshow.TvShowRepository
8 | import kotlinx.coroutines.ExperimentalCoroutinesApi
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.mapLatest
11 | import javax.inject.Inject
12 |
13 |
14 | class GetAiringTodayTvShowsUseCaseImpl @Inject constructor(
15 | private val tvShowRepository: TvShowRepository
16 | ) {
17 | @OptIn(ExperimentalCoroutinesApi::class)
18 | operator fun invoke(deviceLanguage: DeviceLanguage): Flow> {
19 | return tvShowRepository.airingTodayTvShows(deviceLanguage)
20 | .mapLatest { data -> data.map { it } }
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetDiscoverAllTvShowsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import androidx.paging.PagingData
4 | import androidx.paging.map
5 | import com.example.movplayv3.data.model.DeviceLanguage
6 | import com.example.movplayv3.data.model.Presentable
7 | import com.example.movplayv3.data.repository.tvshow.TvShowRepository
8 | import kotlinx.coroutines.ExperimentalCoroutinesApi
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.mapLatest
11 | import javax.inject.Inject
12 |
13 |
14 | class GetDiscoverAllTvShowsUseCaseImpl @Inject constructor(
15 | private val tvShowRepository: TvShowRepository
16 | ) {
17 | @OptIn(ExperimentalCoroutinesApi::class)
18 | operator fun invoke(deviceLanguage: DeviceLanguage): Flow> {
19 | return tvShowRepository.discoverTvShows(deviceLanguage)
20 | .mapLatest { data -> data.map { it } }
21 |
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/browse/tvshows/BrowseTvShowsScreenUIState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.browse.tvshows
2 |
3 | import androidx.compose.runtime.Stable
4 | import androidx.paging.PagingData
5 | import com.example.movplayv3.data.model.Presentable
6 | import com.example.movplayv3.data.model.tvshow.TvShowType
7 | import kotlinx.coroutines.flow.Flow
8 | import kotlinx.coroutines.flow.emptyFlow
9 |
10 | @Stable
11 | data class BrowseTvShowsScreenUIState(
12 | val selectedTvShowType: TvShowType,
13 | val tvShow: Flow>,
14 | val favoriteTvShowsCount: Int
15 | ) {
16 | companion object {
17 | fun getDefault(selectedTvShowType: TvShowType): BrowseTvShowsScreenUIState {
18 | return BrowseTvShowsScreenUIState(
19 | selectedTvShowType = selectedTvShowType,
20 | tvShow = emptyFlow(),
21 | favoriteTvShowsCount = 0
22 | )
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/repository/config/ConfigRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.repository.config
2 |
3 | import com.example.movplayv3.data.model.DeviceLanguage
4 | import com.example.movplayv3.data.model.Genre
5 | import com.example.movplayv3.data.model.ProviderSource
6 | import com.example.movplayv3.utils.ImageUrlParser
7 | import kotlinx.coroutines.flow.Flow
8 |
9 | interface ConfigRepository {
10 | fun isInitialized(): Flow
11 |
12 | fun updateLocale()
13 |
14 | fun getSpeechToTextAvailable(): Flow
15 |
16 | fun getCameraAvailable(): Flow
17 |
18 | fun getDeviceLanguage(): Flow
19 |
20 | fun getImageUrlParser(): Flow
21 |
22 | fun getMoviesGenres(): Flow>
23 |
24 | fun getTvShowGenres(): Flow>
25 |
26 | fun getAllMoviesWatchProviders(): Flow>
27 |
28 | fun getAllTvShowWatchProviders(): Flow>
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/MovieDetailEntity.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.Index
6 | import androidx.room.PrimaryKey
7 | import com.example.movplayv3.data.model.DetailPresentable
8 |
9 | @Entity(indices = [Index(value = ["language"])])
10 | data class MovieDetailEntity(
11 | override val id: Int,
12 | @ColumnInfo(name = "poster_path")
13 | override val posterPath: String?,
14 | override val title: String,
15 | @ColumnInfo(name = "original_title")
16 | val originalTitle: String,
17 | override val adult: Boolean?,
18 | override val overview: String,
19 | @ColumnInfo(name = "backdrop_path")
20 | override val backdropPath: String?,
21 | override val voteAverage: Float,
22 | override val voteCount: Int,
23 | val language: String
24 | ) : DetailPresentable {
25 | @PrimaryKey(autoGenerate = true)
26 | var entityId: Int = 0
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetSeasonDetailsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.model.DeviceLanguage
4 | import com.example.movplayv3.data.model.SeasonDetails
5 | import com.example.movplayv3.data.remote.api.ApiResponse
6 | import com.example.movplayv3.data.remote.api.awaitApiResponse
7 | import com.example.movplayv3.data.repository.season.SeasonRepository
8 | import javax.inject.Inject
9 |
10 | class GetSeasonDetailsUseCaseImpl @Inject constructor(
11 | private val seasonRepository: SeasonRepository
12 | ) {
13 | suspend operator fun invoke(
14 | tvShowId: Int,
15 | seasonNumber: Int,
16 | deviceLanguage: DeviceLanguage
17 | ): ApiResponse {
18 | return seasonRepository.seasonDetails(
19 | tvShowId = tvShowId,
20 | seasonNumber = seasonNumber,
21 | deviceLanguage = deviceLanguage
22 | ).awaitApiResponse()
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetSeasonCreditsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.model.AggregatedCredits
4 | import com.example.movplayv3.data.model.DeviceLanguage
5 | import com.example.movplayv3.data.remote.api.ApiResponse
6 | import com.example.movplayv3.data.remote.api.awaitApiResponse
7 | import com.example.movplayv3.data.repository.season.SeasonRepository
8 | import javax.inject.Inject
9 |
10 | class GetSeasonCreditsUseCaseImpl @Inject constructor(
11 | private val seasonRepository: SeasonRepository
12 | ) {
13 | suspend operator fun invoke(
14 | tvShowId: Int,
15 | seasonNumber: Int,
16 | deviceLanguage: DeviceLanguage
17 | ): ApiResponse {
18 | return seasonRepository.seasonCredits(
19 | tvShowId = tvShowId,
20 | seasonNumber = seasonNumber,
21 | isoCode = deviceLanguage.languageCode
22 | ).awaitApiResponse()
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/GetMediaTypeReviewsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import androidx.paging.PagingData
4 | import com.example.movplayv3.data.model.MediaType
5 | import com.example.movplayv3.data.model.Review
6 | import com.example.movplayv3.data.repository.movie.MovieRepository
7 | import com.example.movplayv3.data.repository.tvshow.TvShowRepository
8 | import kotlinx.coroutines.flow.Flow
9 | import kotlinx.coroutines.flow.emptyFlow
10 | import javax.inject.Inject
11 |
12 | class GetMediaTypeReviewsUseCaseImpl @Inject constructor(
13 | private val movieRepository: MovieRepository,
14 | private val tvShowRepository: TvShowRepository
15 | ) {
16 | operator fun invoke(mediaId: Int, type: MediaType): Flow> {
17 | return when (type) {
18 | MediaType.Movie -> movieRepository.movieReviews(mediaId)
19 | MediaType.Tv -> tvShowRepository.tvShowReviews(mediaId)
20 | else -> emptyFlow()
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/related/movies/RelatedMoviesScreenUiState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.related.movies
2 |
3 | import androidx.compose.runtime.Stable
4 | import androidx.paging.PagingData
5 | import com.example.movplayv3.data.model.RelationType
6 | import com.example.movplayv3.data.model.movie.Movie
7 | import com.example.movplayv3.ui.screens.destinations.MovieScreenDestination
8 | import kotlinx.coroutines.flow.Flow
9 | import kotlinx.coroutines.flow.emptyFlow
10 |
11 | @Stable
12 | data class RelatedMoviesScreenUiState(
13 | val relationType: RelationType,
14 | val movies: Flow>,
15 | val startRoute: String
16 | ) {
17 | companion object {
18 | fun getDefault(relationType: RelationType): RelatedMoviesScreenUiState {
19 | return RelatedMoviesScreenUiState(
20 | relationType = relationType,
21 | movies = emptyFlow(),
22 | startRoute = MovieScreenDestination.route
23 | )
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/local/db/movie/FavoritesMoviesDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.local.db.movie
2 |
3 | import androidx.paging.DataSource
4 | import androidx.room.Dao
5 | import androidx.room.Insert
6 | import androidx.room.OnConflictStrategy
7 | import androidx.room.Query
8 | import com.example.movplayv3.data.model.movie.MovieFavorite
9 | import kotlinx.coroutines.flow.Flow
10 |
11 | @Dao
12 | interface FavoritesMoviesDao {
13 | @Query("SELECT * FROM MovieFavorite ORDER BY added_date DESC")
14 | fun getAllFavoriteMovies(): DataSource.Factory
15 |
16 | @Insert(onConflict = OnConflictStrategy.REPLACE)
17 | suspend fun likeMovie(vararg movieDetails: MovieFavorite)
18 |
19 | @Query("DELETE FROM MovieFavorite WHERE id = :movieId")
20 | suspend fun unlikeMovie(movieId: Int)
21 |
22 | @Query("SELECT id FROM MovieFavorite")
23 | fun favouriteMoviesIds(): Flow>
24 |
25 | @Query("SELECT COUNT(id) FROM MovieFavorite")
26 | fun favouriteMoviesCount(): Flow
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Part.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class Part(
8 | override val id: Int,
9 | override val adult: Boolean?,
10 | @Json(name = "genre_ids")
11 | val genreIds: List,
12 | @Json(name = "original_language")
13 | val originalLanguage: String?,
14 | @Json(name = "original_title")
15 | val originalTitle: String?,
16 | override val overview: String?,
17 | @Json(name = "release_date")
18 | val releaseDate: String?,
19 | @Json(name = "poster_path")
20 | override val posterPath: String?,
21 | val popularity: Float,
22 | override val title: String,
23 | val video: Boolean,
24 | @Json(name = "vote_average")
25 | override val voteAverage: Float,
26 | @Json(name = "vote_count")
27 | override val voteCount: Int
28 | ) : DetailPresentable {
29 | override val backdropPath: String? = null
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/related/tvseries/RelatedTvShowScreenUiState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.related.tvseries
2 |
3 | import androidx.compose.runtime.Stable
4 | import androidx.paging.PagingData
5 | import com.example.movplayv3.data.model.RelationType
6 | import com.example.movplayv3.data.model.tvshow.TvShow
7 | import com.example.movplayv3.ui.screens.destinations.MovieScreenDestination
8 | import kotlinx.coroutines.flow.Flow
9 | import kotlinx.coroutines.flow.emptyFlow
10 |
11 | @Stable
12 | data class RelatedTvShowScreenUiState(
13 | val relationType: RelationType,
14 | val tvShow: Flow>,
15 | val startRoute: String
16 | ) {
17 | companion object {
18 | fun getDefault(relationType: RelationType): RelatedTvShowScreenUiState {
19 | return RelatedTvShowScreenUiState(
20 | relationType = relationType,
21 | tvShow = emptyFlow(),
22 | startRoute = MovieScreenDestination.route
23 | )
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/local/db/tvshow/FavoritesTvShowsDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.local.db.tvshow
2 |
3 | import androidx.paging.DataSource
4 | import androidx.room.Dao
5 | import androidx.room.Insert
6 | import androidx.room.OnConflictStrategy
7 | import androidx.room.Query
8 | import com.example.movplayv3.data.model.tvshow.TvShowFavorite
9 | import kotlinx.coroutines.flow.Flow
10 |
11 | @Dao
12 | interface FavoritesTvShowsDao {
13 | @Query("SELECT * FROM TvShowFavorite ORDER BY added_date DESC ")
14 | fun getAllFavoriteTvShows(): DataSource.Factory
15 |
16 | @Insert(onConflict = OnConflictStrategy.REPLACE)
17 | suspend fun likeTvShow(vararg tvShowDetails: TvShowFavorite)
18 |
19 | @Query("DELETE FROM TvShowFavorite WHERE id = :tvShowId")
20 | suspend fun unlikeTvShow(tvShowId: Int)
21 |
22 | @Query("SELECT id FROM TvShowFavorite")
23 | fun favoriteTvShowIds(): Flow>
24 |
25 | @Query("SELECT COUNT(id) FROM TvShowFavorite")
26 | fun favoriteTvShowCount(): Flow
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/tvshow/TvShowDetailsEntity.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.tvshow
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.Index
6 | import androidx.room.PrimaryKey
7 | import com.example.movplayv3.data.model.DetailPresentable
8 | import com.squareup.moshi.JsonClass
9 |
10 | @Entity(indices = [Index(value = ["language"])])
11 | data class TvShowDetailEntity(
12 | override val id: Int,
13 | @ColumnInfo(name = "poster_path")
14 | override val posterPath: String?,
15 | override val title: String,
16 | @ColumnInfo(name = "original_title")
17 | val originalTitle: String?,
18 | override val adult: Boolean?,
19 | override val overview: String,
20 | @ColumnInfo(name = "backdrop_path")
21 | override val backdropPath: String?,
22 | override val voteAverage: Float,
23 | override val voteCount: Int,
24 | val language: String
25 | ) : DetailPresentable {
26 | @PrimaryKey(autoGenerate = true)
27 | var entityId: Int = 0
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetTvShowReviewsCountUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.remote.api.ApiResponse
4 | import com.example.movplayv3.data.remote.api.awaitApiResponse
5 | import com.example.movplayv3.data.repository.tvshow.TvShowRepository
6 | import javax.inject.Inject
7 |
8 | class GetTvShowReviewsCountUseCaseImpl @Inject constructor(
9 | private val tvShowRepository: TvShowRepository
10 | ) {
11 | suspend operator fun invoke(tvShowId: Int): ApiResponse {
12 | val response = tvShowRepository.tvShowReview(tvShowId).awaitApiResponse()
13 |
14 | return when (response) {
15 | is ApiResponse.Success -> {
16 | val reviewCount = response.data?.totalResults ?: 0
17 | ApiResponse.Success(reviewCount)
18 | }
19 | is ApiResponse.Failure -> ApiResponse.Failure(response.apiError)
20 | is ApiResponse.Exception -> ApiResponse.Exception(response.exception)
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/utils/Converters.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.utils
2 |
3 | import androidx.room.TypeConverter
4 | import com.example.movplayv3.data.model.movie.MovieEntityType
5 | import com.example.movplayv3.data.model.tvshow.TvShowEntityType
6 | import java.util.*
7 |
8 | class DateConverters {
9 | @TypeConverter
10 | fun fromTimestamp(value: Long?): Date? {
11 | return value?.let { Date(it) }
12 | }
13 |
14 | @TypeConverter
15 | fun dateToTimestamp(date: Date?): Long? {
16 | return date?.time
17 | }
18 | }
19 |
20 | class MovieEntityTypeConverters {
21 | @TypeConverter
22 | fun toMovieEntityType(value: String) = enumValueOf(value)
23 |
24 | @TypeConverter
25 | fun fromMovieEntityType(value: MovieEntityType) = value.name
26 | }
27 |
28 | class TvShowEntityTypeConverters {
29 | @TypeConverter
30 | fun toTvShowEntityType(value: String) = enumValueOf(value)
31 |
32 | @TypeConverter
33 | fun fromTvShowEntityType(value: TvShowEntityType) = value.name
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/dialogs/MovplatErrorDialog.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.dialogs
2 |
3 | import androidx.compose.material3.OutlinedButton
4 | import androidx.compose.material3.Text
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.res.stringResource
8 | import com.example.movplayv3.R
9 |
10 | @Composable
11 | fun MovplayErrorDialog(
12 | modifier: Modifier = Modifier,
13 | onDismissRequest: () -> Unit = {},
14 | errorMessage: String? = null,
15 | onConfirmClick: () -> Unit = {}
16 | ) {
17 | MovplayApplicationDialog(
18 | modifier = modifier,
19 | onDismissRequest = onDismissRequest,
20 | infoText = errorMessage ?: stringResource(R.string.error_dialog_default_text),
21 | confirmButton = {
22 | OutlinedButton(
23 | onClick = onConfirmClick
24 | ) {
25 | Text(text = stringResource(R.string.exit_dialog_confirm_button_label))
26 | }
27 | }
28 | )
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/repository/favorites/FavoritesRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.repository.favorites
2 |
3 | import androidx.paging.PagingData
4 | import com.example.movplayv3.data.model.movie.MovieDetails
5 | import com.example.movplayv3.data.model.movie.MovieFavorite
6 | import com.example.movplayv3.data.model.tvshow.TvShowDetails
7 | import com.example.movplayv3.data.model.tvshow.TvShowFavorite
8 | import kotlinx.coroutines.flow.Flow
9 |
10 | interface FavoritesRepository {
11 | fun likeMovie(movieDetails: MovieDetails)
12 |
13 | fun likeTvShow(tvShowDetails: TvShowDetails)
14 |
15 | fun unlikeMovie(movieDetails: MovieDetails)
16 |
17 | fun unlikeTvShows(tvShowDetails: TvShowDetails)
18 |
19 | fun favoriteMovies(): Flow>
20 |
21 | fun favoriteTvShows(): Flow>
22 |
23 | fun getFavoriteMoviesIds(): Flow>
24 |
25 | fun getFavoriteTvShowsIds(): Flow>
26 |
27 | fun getFavoriteMoviesCount(): Flow
28 |
29 | fun getFavoriteTvShowsCount(): Flow
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetMovieReviewsCountUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.remote.api.ApiResponse
4 | import com.example.movplayv3.data.remote.api.awaitApiResponse
5 | import com.example.movplayv3.data.repository.movie.MovieRepository
6 | import javax.inject.Inject
7 |
8 | class GetMovieReviewsCountUseCaseImpl @Inject constructor(
9 | private val movieRepository: MovieRepository
10 | ) {
11 | suspend operator fun invoke(movieId: Int): ApiResponse {
12 | val response = movieRepository
13 | .movieReview(movieId)
14 | .awaitApiResponse()
15 |
16 | return when (response) {
17 | is ApiResponse.Success -> {
18 | val reviewsCount = response.data?.totalResults ?: 0
19 | ApiResponse.Success(reviewsCount)
20 | }
21 | is ApiResponse.Failure -> ApiResponse.Failure(response.apiError)
22 | is ApiResponse.Exception -> ApiResponse.Exception(response.exception)
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/GetFavoritesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase
2 |
3 | import androidx.paging.PagingData
4 | import androidx.paging.map
5 | import com.example.movplayv3.data.model.FavoriteType
6 | import com.example.movplayv3.data.model.Presentable
7 | import com.example.movplayv3.data.repository.favorites.FavoritesRepository
8 | import kotlinx.coroutines.ExperimentalCoroutinesApi
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.mapLatest
11 | import javax.inject.Inject
12 |
13 |
14 | class GetFavoritesUseCaseImpl @Inject constructor(
15 | private val favoritesRepository: FavoritesRepository
16 | ) {
17 | @OptIn(ExperimentalCoroutinesApi::class)
18 | operator fun invoke(type: FavoriteType): Flow> {
19 | val favorites = when (type) {
20 | FavoriteType.Movie -> favoritesRepository.favoriteMovies()
21 | FavoriteType.TvShow -> favoritesRepository.favoriteTvShows()
22 | }.mapLatest { data -> data.map { it } }
23 |
24 | return favorites
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_twitter.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/WatchProviders.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class WatchProvidersResponse(
8 | val id: Int,
9 | val results: Map
10 | )
11 |
12 | @JsonClass(generateAdapter = true)
13 | data class AllWatchProvidersResponse(
14 | val results: List
15 | )
16 |
17 | @JsonClass(generateAdapter = true)
18 | data class WatchProviders(
19 | val link: String,
20 |
21 | val rent: List?,
22 |
23 | val buy: List?,
24 |
25 | val flatrate: List?
26 | )
27 |
28 | @JsonClass(generateAdapter = true)
29 | data class ProviderSource(
30 | @Json(name = "display_priority")
31 | val displayPriority: Int,
32 |
33 | @Json(name = "logo_path")
34 | val logoPath: String,
35 |
36 | @Json(name = "provider_id")
37 | val providerId: Int,
38 |
39 | @Json(name = "provider_name")
40 | val providerName: String
41 | )
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetMovieBackdropsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.model.Image
4 | import com.example.movplayv3.data.remote.api.ApiResponse
5 | import com.example.movplayv3.data.remote.api.awaitApiResponse
6 | import com.example.movplayv3.data.repository.movie.MovieRepository
7 | import javax.inject.Inject
8 |
9 | class GetMovieBackdropsUseCaseImpl @Inject constructor(
10 | private val movieRepository: MovieRepository
11 | ) {
12 | suspend operator fun invoke(movieId: Int): ApiResponse> {
13 | val response = movieRepository.movieImages(movieId).awaitApiResponse()
14 |
15 | return when (response) {
16 | is ApiResponse.Success -> {
17 | val backdrops = response.data?.backdrops ?: emptyList()
18 | ApiResponse.Success(backdrops)
19 | }
20 | is ApiResponse.Failure -> ApiResponse.Failure(response.apiError)
21 | is ApiResponse.Exception -> ApiResponse.Exception(response.exception)
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/tvshow/GetTvShowImagesUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.tvshow
2 |
3 | import com.example.movplayv3.data.model.Image
4 | import com.example.movplayv3.data.remote.api.ApiResponse
5 | import com.example.movplayv3.data.remote.api.awaitApiResponse
6 | import com.example.movplayv3.data.repository.tvshow.TvShowRepository
7 | import javax.inject.Inject
8 |
9 | class GetTvShowImagesUseCaseImpl @Inject constructor(
10 | private val tvShowRepository: TvShowRepository
11 | ) {
12 | suspend operator fun invoke(tvShowId: Int): ApiResponse> {
13 | val response = tvShowRepository.tvShowImages(tvShowId).awaitApiResponse()
14 |
15 | return when (response) {
16 | is ApiResponse.Success -> {
17 | val images = response.data?.backdrops ?: emptyList()
18 | ApiResponse.Success(images)
19 | }
20 | is ApiResponse.Failure -> ApiResponse.Failure(response.apiError)
21 | is ApiResponse.Exception -> ApiResponse.Exception(response.exception)
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/utils/ColorUtils.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.utils
2 |
3 | import android.graphics.Bitmap
4 | import androidx.compose.ui.graphics.ColorFilter
5 | import androidx.compose.ui.graphics.ColorMatrix
6 | import androidx.palette.graphics.Palette
7 |
8 | fun Bitmap.isDark(): Boolean {
9 | return Palette.from(this).generate().dominantSwatch?.run {
10 | getPerceptiveLuminance() < 0.5
11 | } ?: true
12 | }
13 |
14 | fun Palette.Swatch.getPerceptiveLuminance(): Float {
15 | val r: Int = android.graphics.Color.red(rgb)
16 | val g: Int = android.graphics.Color.green(rgb)
17 | val b: Int = android.graphics.Color.blue(rgb)
18 |
19 | return ((0.299f * r) + (0.587f * g) + (0.114f * b)) / 255f
20 | }
21 |
22 | fun ColorFilter.Companion.grayScale(): ColorFilter {
23 | val grayScaleMatrix = ColorMatrix(
24 | floatArrayOf(
25 | 0.33f, 0.33f, 0.33f, 0f, 0f,
26 | 0.33f, 0.33f, 0.33f, 0f, 0f,
27 | 0.33f, 0.33f, 0.33f, 0f, 0f,
28 | 0f, 0f, 0f, 1f, 0f
29 | )
30 | )
31 |
32 | return colorMatrix(grayScaleMatrix)
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/BaseViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.example.movplayv3.data.remote.api.ApiResponse
6 | import com.google.firebase.crashlytics.FirebaseCrashlytics
7 | import kotlinx.coroutines.flow.*
8 | import kotlinx.coroutines.launch
9 |
10 | open class BaseViewModel : ViewModel() {
11 | private val _error: MutableSharedFlow = MutableSharedFlow(replay = 0)
12 | val error: StateFlow = _error.asSharedFlow().stateIn(
13 | viewModelScope, SharingStarted.WhileSubscribed(10), null
14 | )
15 |
16 | protected fun onError(response: ApiResponse.Exception) {
17 | FirebaseCrashlytics.getInstance().recordException(response.exception)
18 |
19 | viewModelScope.launch {
20 | _error.emit(response.exception.localizedMessage)
21 | }
22 | }
23 |
24 | protected fun onFailure(response: ApiResponse.Failure) {
25 | viewModelScope.launch {
26 | _error.emit(response.apiError.statusMessage)
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/Episode.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 | import java.util.*
6 | import java.util.concurrent.TimeUnit
7 |
8 | @JsonClass(generateAdapter = true)
9 | data class Episode(
10 | val id: Int,
11 | @Json(name = "air_date")
12 | val airDate: Date?,
13 | @Json(name = "episode_number")
14 | val episodeNumber: Int,
15 | val name: String,
16 | val overview: String,
17 | @Json(name = "production_code")
18 | val productionCode: String,
19 | @Json(name = "season_number")
20 | val seasonNumber: Int,
21 | @Json(name = "still_path")
22 | val stillPath: String?,
23 | @Json(name = "vote_average")
24 | val voteAverage: Float,
25 | @Json(name = "vote_count")
26 | val voteCount: Int
27 | ) {
28 | fun isReleased(): Boolean {
29 | if (airDate == null) {
30 | return false
31 | }
32 | val timeDiff = Calendar.getInstance().timeInMillis - airDate.time
33 | return TimeUnit.MILLISECONDS.toDays(timeDiff) >= 0
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/Movie.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | import com.example.movplayv3.data.model.DetailPresentable
4 | import com.squareup.moshi.Json
5 | import com.squareup.moshi.JsonClass
6 |
7 | @JsonClass(generateAdapter = true)
8 | data class Movie(
9 | override val id: Int,
10 | @Json(name = "poster_path")
11 | override val posterPath: String?,
12 | override val adult: Boolean?,
13 | override val overview: String,
14 | @Json(name = "release_date")
15 | val releaseDate: String?,
16 | @Json(name = "genre_ids")
17 | val genreIds: List,
18 | @Json(name = "original_title")
19 | val originalTitle: String,
20 | @Json(name = "original_language")
21 | val originalLanguage: String,
22 | override val title: String,
23 | @Json(name = "backdrop_path")
24 | override val backdropPath: String?,
25 | val popularity: Float?,
26 | @Json(name = "vote_count")
27 | override val voteCount: Int,
28 | val video: Boolean,
29 | @Json(name = "vote_average")
30 | override val voteAverage: Float
31 | ) : DetailPresentable
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/ExternalIds.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class ExternalIds(
8 | val id: Int,
9 | @Json(name = "imdb_id")
10 | val imdbId: String?,
11 |
12 | @Json(name = "facebook_id")
13 | val facebookId: String?,
14 |
15 | @Json(name = "instagram_id")
16 | val instagramId: String?,
17 |
18 | @Json(name = "twitter_id")
19 | val twitterId: String?
20 | ) {
21 | fun toExternalIdList(type: ExternalContentType): List {
22 | return buildList {
23 | facebookId?.let { id ->
24 | add(ExternalId.Facebook(id, type))
25 | }
26 |
27 | instagramId?.let { id ->
28 | add(ExternalId.Instagram(id, type))
29 | }
30 |
31 | twitterId?.let { id ->
32 | add(ExternalId.Twitter(id, type))
33 | }
34 |
35 | imdbId?.let { id ->
36 | add(ExternalId.Imdb(id, type))
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/button/MovplayScrollToStartButton.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.button
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.shape.CircleShape
5 | import androidx.compose.material.icons.Icons
6 | import androidx.compose.material.icons.filled.ArrowBack
7 | import androidx.compose.material3.Icon
8 | import androidx.compose.material3.IconButton
9 | import androidx.compose.material3.MaterialTheme
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.draw.clip
13 |
14 | @Composable
15 | fun MovplayScrollToStartButton(
16 | modifier: Modifier = Modifier,
17 | onClick: () -> Unit = {}
18 | ) {
19 | IconButton(
20 | modifier = modifier
21 | .clip(shape = CircleShape)
22 | .background(MaterialTheme.colorScheme.surface.copy(alpha = 0.5f)),
23 | onClick = onClick
24 | ) {
25 | Icon(
26 | imageVector = Icons.Filled.ArrowBack,
27 | contentDescription = "scroll to start",
28 | tint = MaterialTheme.colorScheme.primary
29 | )
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/domain/usecase/movie/GetMovieExternalIdsUseCaseImpl.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.domain.usecase.movie
2 |
3 | import com.example.movplayv3.data.model.ExternalContentType
4 | import com.example.movplayv3.data.model.ExternalId
5 | import com.example.movplayv3.data.remote.api.ApiResponse
6 | import com.example.movplayv3.data.remote.api.awaitApiResponse
7 | import com.example.movplayv3.data.repository.movie.MovieRepository
8 | import javax.inject.Inject
9 |
10 |
11 | class GetMovieExternalIdsUseCaseImpl @Inject constructor(
12 | private val movieRepository: MovieRepository
13 | ) {
14 | suspend operator fun invoke(movieId: Int): ApiResponse> {
15 | val response = movieRepository.getExternalIds(movieId).awaitApiResponse()
16 |
17 | return when (response) {
18 | is ApiResponse.Success -> {
19 | val ids = response.data?.toExternalIdList(ExternalContentType.Movie)
20 | ApiResponse.Success(ids)
21 | }
22 | is ApiResponse.Failure -> ApiResponse.Failure(response.apiError)
23 | is ApiResponse.Exception -> ApiResponse.Exception(response.exception)
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/repository/season/SeasonRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.repository.season
2 |
3 | import com.example.movplayv3.data.model.*
4 | import com.example.movplayv3.data.model.tvshow.TvSeasonsResponse
5 | import retrofit2.Call
6 |
7 | interface SeasonRepository {
8 | fun getTvSeason(
9 | tvShowId: Int,
10 | seasonNumber: Int,
11 | deviceLanguage: DeviceLanguage = DeviceLanguage.default
12 | ): Call
13 |
14 | fun seasonDetails(
15 | tvShowId: Int,
16 | seasonNumber: Int,
17 | deviceLanguage: DeviceLanguage = DeviceLanguage.default
18 | ): Call
19 |
20 | fun episodesImage(
21 | tvShowId: Int,
22 | seasonNumber: Int,
23 | episodeNumber: Int
24 | ): Call
25 |
26 | fun seasonVideos(
27 | tvShowId: Int,
28 | seasonNumber: Int,
29 | isoCode: String = DeviceLanguage.default.languageCode
30 | ): Call
31 |
32 | fun seasonCredits(
33 | tvShowId: Int,
34 | seasonNumber: Int,
35 | isoCode: String = DeviceLanguage.default.languageCode
36 | ): Call
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/components/texts/MovplayLabeledText.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.components.texts
2 |
3 | import androidx.compose.foundation.layout.Arrangement
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.material3.MaterialTheme
6 | import androidx.compose.material3.Text
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.text.font.FontWeight
11 | import androidx.compose.ui.unit.Dp
12 | import androidx.compose.ui.unit.sp
13 | import com.example.movplayv3.ui.theme.spacing
14 |
15 | @Composable
16 | fun MovplayLabeledText(
17 | label: String,
18 | text: String,
19 | modifier: Modifier = Modifier,
20 | spacing: Dp = MaterialTheme.spacing.default
21 | ) {
22 | Column(
23 | modifier = modifier,
24 | verticalArrangement = Arrangement.spacedBy(spacing)
25 | ) {
26 | Text(
27 | text = label,
28 | // fontSize = 12.sp,
29 | fontWeight = FontWeight.ExtraBold
30 | )
31 | Text(
32 | text = text,
33 | // fontSize = 12.sp
34 | )
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/search/SearchScreenUIState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.search
2 |
3 | import androidx.compose.runtime.Stable
4 |
5 | @Stable
6 | data class SearchScreenUIState(
7 | val searchOptionsState: SearchOptionsState,
8 | val query: String?,
9 | val suggestions: List,
10 | val searchState: SearchState,
11 | val resultState: ResultState,
12 | val queryLoading: Boolean
13 | ) {
14 | companion object {
15 | val default: SearchScreenUIState = SearchScreenUIState(
16 | searchOptionsState = SearchOptionsState.default,
17 | query = null,
18 | suggestions = emptyList(),
19 | searchState = SearchState.EmptyQuery,
20 | resultState = ResultState.Default(),
21 | queryLoading = false
22 | )
23 | }
24 | }
25 |
26 | @Stable
27 | data class SearchOptionsState(
28 | val voiceSearchAvailable: Boolean,
29 | val cameraSearchAvailable: Boolean
30 | ){
31 | companion object{
32 | val default: SearchOptionsState = SearchOptionsState(
33 | voiceSearchAvailable = false,
34 | cameraSearchAvailable = false
35 | )
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/data/model/movie/MovieStatus.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.data.model.movie
2 |
3 | import androidx.annotation.StringRes
4 | import com.example.movplayv3.R
5 | import com.squareup.moshi.Json
6 | import com.squareup.moshi.JsonClass
7 |
8 | @JsonClass(generateAdapter = false)
9 | enum class MovieStatus(val value: String) {
10 | @Json(name = "Rumored")
11 | Rumored("Rumored"),
12 |
13 | @Json(name = "Planned")
14 | Planned("Planned"),
15 |
16 | @Json(name = "In Production")
17 | InProduction("In Production"),
18 |
19 | @Json(name = "Post Production")
20 | PostProduction("Post Production"),
21 |
22 | @Json(name = "Released")
23 | Released("Released"),
24 |
25 | @Json(name = "Cancelled")
26 | Cancelled("Cancelled");
27 |
28 | @StringRes
29 | fun getLabel(): Int = when (this) {
30 | Rumored -> R.string.status_rumored_label
31 | Planned -> R.string.status_planned_label
32 | InProduction -> R.string.status_in_production_label
33 | PostProduction -> R.string.status_post_production_label
34 | Released -> R.string.status_released_label
35 | Cancelled -> R.string.status_cancelled_label
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/details/person/PersonDetailsScreenUIState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.details.person
2 |
3 | import androidx.compose.runtime.Stable
4 | import com.example.movplayv3.data.model.CombinedCredits
5 | import com.example.movplayv3.data.model.ExternalId
6 | import com.example.movplayv3.data.model.PersonDetails
7 |
8 | @Stable
9 | data class PersonDetailsScreenUIState(
10 | val startRoute: String,
11 | val details: PersonDetails?,
12 | val externalIds: List?,
13 | val credits: CombinedCredits?,
14 | val error: String?
15 | ) {
16 | companion object {
17 | fun getDefault(startRoute: String): PersonDetailsScreenUIState {
18 | return PersonDetailsScreenUIState(
19 | startRoute = startRoute,
20 | details = null,
21 | externalIds = null,
22 | credits = null,
23 | error = null
24 | )
25 | }
26 | }
27 | }
28 |
29 | @Stable
30 | sealed class PersonDetailsState {
31 | object Loading : PersonDetailsState()
32 | object Error : PersonDetailsState()
33 | data class Result(val details: PersonDetails) : PersonDetailsState()
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/movplayv3/ui/screens/seasons/SeasonDetailsScreenUiState.kt:
--------------------------------------------------------------------------------
1 | package com.example.movplayv3.ui.screens.seasons
2 |
3 | import androidx.compose.runtime.Stable
4 | import com.example.movplayv3.data.model.AggregatedCredits
5 | import com.example.movplayv3.data.model.Image
6 | import com.example.movplayv3.data.model.SeasonDetails
7 | import com.example.movplayv3.data.model.Video
8 | import com.example.movplayv3.ui.screens.destinations.TvShowScreenDestination
9 |
10 | @Stable
11 | data class SeasonDetailsScreenUiState(
12 | val startRoute: String,
13 | val seasonDetails: SeasonDetails?,
14 | val aggregatedCredits: AggregatedCredits?,
15 | val videos: List