From 45020832ff9cdb87391d55edb7ddcc4350dea2c7 Mon Sep 17 00:00:00 2001 From: Owen LeJeune Date: Thu, 10 Mar 2022 13:22:57 -0500 Subject: [PATCH] custom dropdown menu + setup account api service --- .../owenlejeune/tvtime/api/tmdb/AccountApi.kt | 74 +++++++++++++++++++ .../tvtime/api/tmdb/AccountService.kt | 50 +++++++++++++ .../tvtime/api/tmdb/DetailService.kt | 4 +- .../tvtime/api/tmdb/MarkAsFavoriteBody.kt | 9 +++ .../owenlejeune/tvtime/api/tmdb/MoviesApi.kt | 8 +- .../tvtime/api/tmdb/MoviesService.kt | 4 +- .../owenlejeune/tvtime/api/tmdb/TmdbClient.kt | 13 +++- .../com/owenlejeune/tvtime/api/tmdb/TvApi.kt | 8 +- .../owenlejeune/tvtime/api/tmdb/TvService.kt | 4 +- .../tvtime/api/tmdb/model/AccountDetails.kt | 21 ++++++ .../tvtime/api/tmdb/model/AccountList.kt | 13 ++++ .../api/tmdb/model/AccountListResponse.kt | 10 +++ .../tvtime/api/tmdb/model/FavoriteMedia.kt | 18 +++++ .../api/tmdb/model/FavoriteMediaResponse.kt | 10 +++ .../tvtime/api/tmdb/model/FavoriteMovie.kt | 23 ++++++ .../tvtime/api/tmdb/model/FavoriteTvSeries.kt | 22 ++++++ .../{RatingResponse.kt => StatusResponse.kt} | 2 +- .../tvtime/api/tmdb/model/WatchlistBody.kt | 9 +++ .../tvtime/api/tmdb/model/WatchlistMedia.kt | 18 +++++ .../tvtime/api/tmdb/model/WatchlistMovie.kt | 23 ++++++ .../api/tmdb/model/WatchlistResponse.kt | 10 +++ .../api/tmdb/model/WatchlistTvSeries.kt | 22 ++++++ .../owenlejeune/tvtime/ui/components/Menus.kt | 66 ++++++++++++++++- .../ui/screens/tabs/bottom/AccountTab.kt | 58 ++++++--------- 24 files changed, 444 insertions(+), 55 deletions(-) create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/AccountApi.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/AccountService.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MarkAsFavoriteBody.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountDetails.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountList.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountListResponse.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMedia.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMediaResponse.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMovie.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteTvSeries.kt rename app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/{RatingResponse.kt => StatusResponse.kt} (90%) create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistBody.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistMedia.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistMovie.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistResponse.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistTvSeries.kt diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/AccountApi.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/AccountApi.kt new file mode 100644 index 0000000..5a92b85 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/AccountApi.kt @@ -0,0 +1,74 @@ +package com.owenlejeune.tvtime.api.tmdb + +import com.owenlejeune.tvtime.api.tmdb.model.* +import retrofit2.Response +import retrofit2.http.* + +interface AccountApi { + + @GET("account") + suspend fun getAccountDetails(): Response + + @GET("account/{id}/lists") + suspend fun getLists( + @Path("id") id: Int, + @Query("page") page: Int + ): Response + + @GET("account/{id}/favorite/movies") + suspend fun getFavoriteMovies( + @Path("id") id: Int, + @Query("page") page: Int + ): Response> + + @GET("account/{id}/favorite/tv") + suspend fun getFavoriteTvShows( + @Path("id") id: Int, + @Query("page") page: Int + ): Response> + +// @Headers("Content-Type: application/json;charset=utf-8") + @POST("account/{id}/favorite") + suspend fun markAsFavorite( + @Path("id") id: Int, + @Body body: MarkAsFavoriteBody + ): Response + + @GET("account/{id}/rated/movies") + suspend fun getRatedMovies( + @Path("id") id: Int, + @Query("page") page: Int + ): Response> + + @GET("account/{id}/rated/tv") + suspend fun getRatedTvShows( + @Path("id") id: Int, + @Query("page") page: Int + ): Response> + + @GET("account/{id}/rated/tv/episodes") + suspend fun getRatedTvEpisodes( + @Path("id") id: Int, + @Query("page") page: Int + ): Response> + + @GET("account/{id}/watchlist/movies") + suspend fun getMovieWatchlist( + @Path("id") id: Int, + @Query("page") page: Int + ): Response> + + @GET("account/{id}/watchlist/tv") + suspend fun getTvWatchlist( + @Path("id") id: Int, + @Query("page") page: Int + ): Response> + +// @Headers("Content-Type: application/json;charset=utf-8") + @POST("account/{id}/watchlist") + suspend fun addToWatchlist( + @Path("id") id: Int, + @Body body: WatchlistBody + ): Response + +} \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/AccountService.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/AccountService.kt new file mode 100644 index 0000000..f9c1f68 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/AccountService.kt @@ -0,0 +1,50 @@ +package com.owenlejeune.tvtime.api.tmdb + +import com.owenlejeune.tvtime.api.tmdb.model.* +import retrofit2.Response + +class AccountService { + + private val accountService by lazy { TmdbClient().createAccountService() } + + suspend fun getAccountDetails(): Response { + return accountService.getAccountDetails() + } + + suspend fun getFavoriteMovies(accountId: Int, page: Int = 1): Response> { + return accountService.getFavoriteMovies(accountId, page) + } + + suspend fun getFavoriteTvShows(accountId: Int, page: Int = 1): Response> { + return accountService.getFavoriteTvShows(accountId, page) + } + + suspend fun markAsFavorite(accountId: Int, body: MarkAsFavoriteBody): Response { + return accountService.markAsFavorite(accountId, body) + } + + suspend fun getRatedMovies(accountId: Int, page: Int = 1): Response> { + return accountService.getRatedMovies(accountId, page) + } + + suspend fun getRatedTvShows(accountId: Int, page: Int = 1): Response> { + return accountService.getRatedTvShows(accountId, page) + } + + suspend fun getRatedTvEpisodes(accountId: Int, page: Int = 1): Response> { + return accountService.getRatedTvEpisodes(accountId, page) + } + + suspend fun getMovieWatchlist(accountId: Int, page: Int = 1): Response> { + return accountService.getMovieWatchlist(accountId, page) + } + + suspend fun getTvWatchlist(accountId: Int, page: Int = 1): Response> { + return accountService.getTvWatchlist(accountId, page) + } + + suspend fun addToWatchlist(accountId: Int, body: WatchlistBody): Response { + return accountService.addToWatchlist(accountId, body) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/DetailService.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/DetailService.kt index f819260..1a6f8e2 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/DetailService.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/DetailService.kt @@ -17,9 +17,9 @@ interface DetailService { suspend fun getReviews(id: Int): Response - suspend fun postRating(id: Int, ratingBody: RatingBody): Response + suspend fun postRating(id: Int, ratingBody: RatingBody): Response - suspend fun deleteRating(id: Int): Response + suspend fun deleteRating(id: Int): Response suspend fun getKeywords(id: Int): Response diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MarkAsFavoriteBody.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MarkAsFavoriteBody.kt new file mode 100644 index 0000000..30359a6 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MarkAsFavoriteBody.kt @@ -0,0 +1,9 @@ +package com.owenlejeune.tvtime.api.tmdb + +import com.google.gson.annotations.SerializedName + +class MarkAsFavoriteBody( + @SerializedName("media_type") val mediaType: String, // media type + @SerializedName("media_id") val mediaId: Int, + @SerializedName("favorite") val isFavorite: Boolean +) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesApi.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesApi.kt index 0d4af9e..57adeda 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesApi.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesApi.kt @@ -47,25 +47,25 @@ interface MoviesApi { @Path("id") id: Int, @Query("guest_session_id") guestSessionId: String, @Body ratingBody: RatingBody - ): Response + ): Response @POST("movie/{id}/rating") suspend fun postMovieRatingAsUser( @Path("id") id: Int, @Query("session_id") sessionId: String, @Body ratingBody: RatingBody - ): Response + ): Response @DELETE("movie/{id}/rating") suspend fun deleteMovieReviewAsGuest( @Path("id") id: Int, @Query("guest_session_id") guestSessionId: String - ): Response + ): Response @DELETE("movie/{id}/rating") suspend fun deleteMovieReviewAsUser( @Path("id") id: Int, @Query("session_id") sessionId: String - ): Response + ): Response } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesService.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesService.kt index e236d73..9f77df2 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesService.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesService.kt @@ -53,7 +53,7 @@ class MoviesService: KoinComponent, DetailService, HomePageService { return movieService.getReviews(id) } - override suspend fun postRating(id: Int, rating: RatingBody): Response { + override suspend fun postRating(id: Int, rating: RatingBody): Response { val session = SessionManager.currentSession ?: throw Exception("Session must not be null") return if (!session.isAuthorized) { movieService.postMovieRatingAsGuest(id, session.sessionId, rating) @@ -62,7 +62,7 @@ class MoviesService: KoinComponent, DetailService, HomePageService { } } - override suspend fun deleteRating(id: Int): Response { + override suspend fun deleteRating(id: Int): Response { val session = SessionManager.currentSession ?: throw Exception("Session must not be null") return if (!session.isAuthorized) { movieService.deleteMovieReviewAsGuest(id, session.sessionId) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt index bc8e0cf..9ab9fb9 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt @@ -5,6 +5,7 @@ import com.owenlejeune.tvtime.BuildConfig import com.owenlejeune.tvtime.api.Client import com.owenlejeune.tvtime.api.QueryParam import com.owenlejeune.tvtime.extensions.addQueryParams +import com.owenlejeune.tvtime.utils.SessionManager import okhttp3.Interceptor import okhttp3.Response import org.koin.core.component.KoinComponent @@ -43,6 +44,10 @@ class TmdbClient: KoinComponent { return client.create(GuestSessionApi::class.java) } + fun createAccountService(): AccountApi { + return client.create(AccountApi::class.java) + } + private inner class TmdbInterceptor: Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val apiParam = QueryParam("api_key", BuildConfig.TMDB_ApiKey) @@ -51,7 +56,13 @@ class TmdbClient: KoinComponent { val languageCode = "${locale.language}-${locale.region}" val languageParam = QueryParam("language", languageCode) - val request = chain.addQueryParams(apiParam, languageParam) + var sessionIdParam: QueryParam? = null + val segments = chain.request().url().encodedPathSegments() + if (segments.size > 1 && segments[1].equals("account") && SessionManager.currentSession?.isAuthorized == true) { + sessionIdParam = QueryParam("session_id", SessionManager.currentSession!!.sessionId) + } + + val request = chain.addQueryParams(apiParam, languageParam, sessionIdParam) return chain.proceed(request) } diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvApi.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvApi.kt index d0a3c19..366eddf 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvApi.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvApi.kt @@ -47,25 +47,25 @@ interface TvApi { @Path("id") id: Int, @Query("guest_session_id") guestSessionId: String, @Body ratingBody: RatingBody - ): Response + ): Response @POST("tv/{id}/rating") suspend fun postTvRatingAsUser( @Path("id") id: Int, @Query("session_id") sessionId: String, @Body ratingBody: RatingBody - ): Response + ): Response @DELETE("tv/{id}/rating") suspend fun deleteTvReviewAsGuest( @Path("id") id: Int, @Query("guest_session_id") guestSessionId: String - ): Response + ): Response @DELETE("tv/{id}/rating") suspend fun deleteTvReviewAsUser( @Path("id") id: Int, @Query("session_id") sessionId: String - ): Response + ): Response } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvService.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvService.kt index ab60f21..1462971 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvService.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvService.kt @@ -53,7 +53,7 @@ class TvService: KoinComponent, DetailService, HomePageService { return service.getReviews(id) } - override suspend fun postRating(id: Int, rating: RatingBody): Response { + override suspend fun postRating(id: Int, rating: RatingBody): Response { val session = SessionManager.currentSession ?: throw Exception("Session must not be null") return if (!session.isAuthorized) { service.postTvRatingAsGuest(id, session.sessionId, rating) @@ -62,7 +62,7 @@ class TvService: KoinComponent, DetailService, HomePageService { } } - override suspend fun deleteRating(id: Int): Response { + override suspend fun deleteRating(id: Int): Response { val session = SessionManager.currentSession ?: throw Exception("Session must not be null") return if (!session.isAuthorized) { service.deleteTvReviewAsGuest(id, session.sessionId) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountDetails.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountDetails.kt new file mode 100644 index 0000000..f667378 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountDetails.kt @@ -0,0 +1,21 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class AccountDetails( + @SerializedName("avatar") val avatar: Avatar, + @SerializedName("id") val id: Int, + @SerializedName("iso_639_1") val languageCode: String, + @SerializedName("iso_3166_1") val countryCode: String, + @SerializedName("name") val name: String, + @SerializedName("include_adult") val includeAdult: Boolean, + @SerializedName("username") val username: String +) + +class Avatar( + @SerializedName("gravatar") val gravatar: Gravatar +) + +class Gravatar( + @SerializedName("hash") val hash: String +) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountList.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountList.kt new file mode 100644 index 0000000..ee9c55f --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountList.kt @@ -0,0 +1,13 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class AccountList( + @SerializedName("description") val description: String, + @SerializedName("favorite_count") val favoriteCount: Int, + @SerializedName("id") val id: Int, + @SerializedName("item_count") val itemCount: Int, + @SerializedName("iso_639_1") val languageCode: String, + @SerializedName("list_type") val listType: String, // media type + @SerializedName("name") val name: String +) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountListResponse.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountListResponse.kt new file mode 100644 index 0000000..043e3a2 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/AccountListResponse.kt @@ -0,0 +1,10 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class AccountListResponse( + @SerializedName("page") val page: Int, + @SerializedName("total_pages") val totalPages: Int, + @SerializedName("total_results") val totalResults: Int, + @SerializedName("results") val results: List +) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMedia.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMedia.kt new file mode 100644 index 0000000..44bb9bb --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMedia.kt @@ -0,0 +1,18 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +abstract class FavoriteMedia( + @SerializedName("poster_path") val posterPath: String?, + @SerializedName("popularity") val popularity: Float, + @SerializedName("id") val id: Int, + @SerializedName("backdrop_path") val backdropPath: String?, + @SerializedName("vote_average") val voteAverage: Float, + @SerializedName("overview") val overview: String, + @SerializedName("release_date", alternate = ["first_air_date"]) val releaseDate: String, + @SerializedName("genre_ids") val genreIds: List, + @SerializedName("original_language") val originalLanguage: String, + @SerializedName("vote_count") val voteCount: Int, + @SerializedName("title", alternate = ["name"]) val title: String, + @SerializedName("original_title", alternate = ["original_name"]) val originalTitle: String +) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMediaResponse.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMediaResponse.kt new file mode 100644 index 0000000..51ec5b7 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMediaResponse.kt @@ -0,0 +1,10 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class FavoriteMediaResponse( + @SerializedName("page") val page: Int, + @SerializedName("results") val results: List, + @SerializedName("total_pages") val totalPages: Int, + @SerializedName("total_results") val totalResults: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMovie.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMovie.kt new file mode 100644 index 0000000..d8a04f6 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteMovie.kt @@ -0,0 +1,23 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class FavoriteMovie( + posterPath: String?, + popularity: Float, + id: Int, + backdropPath: String?, + voteAverage: Float, + overview: String, + releaseDate: String, + genreIds: List, + originalLanguage: String, + voteCount: Int, + title: String, + originalTitle: String, + @SerializedName("adult") val isAdult: Boolean, + @SerializedName("video") val isVideo: Boolean, +): FavoriteMedia( + posterPath, popularity, id, backdropPath, voteAverage, overview, releaseDate, + genreIds, originalLanguage, voteCount, title, originalTitle +) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteTvSeries.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteTvSeries.kt new file mode 100644 index 0000000..0d777a1 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/FavoriteTvSeries.kt @@ -0,0 +1,22 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class FavoriteTvSeries( + posterPath: String?, + popularity: Float, + id: Int, + backdropPath: String?, + voteAverage: Float, + overview: String, + releaseDate: String, + genreIds: List, + originalLanguage: String, + voteCount: Int, + title: String, + originalTitle: String, + @SerializedName("origin_country") val originCountry: List +): FavoriteMedia( + posterPath, popularity, id, backdropPath, voteAverage, overview, releaseDate, + genreIds, originalLanguage, voteCount, title, originalTitle +) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/RatingResponse.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/StatusResponse.kt similarity index 90% rename from app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/RatingResponse.kt rename to app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/StatusResponse.kt index 6577f01..3ad87fb 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/RatingResponse.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/StatusResponse.kt @@ -2,7 +2,7 @@ package com.owenlejeune.tvtime.api.tmdb.model import com.google.gson.annotations.SerializedName -class RatingResponse( +class StatusResponse( @SerializedName("status_code") val statusCode: Int, @SerializedName("status_message") val statusMessage: String ) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistBody.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistBody.kt new file mode 100644 index 0000000..aaf58a3 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistBody.kt @@ -0,0 +1,9 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class WatchlistBody( + @SerializedName("media_type") val mediaType: String, // media type + @SerializedName("media_id") val mediaId: Int, + @SerializedName("watchlist") val onWatchlist: Boolean +) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistMedia.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistMedia.kt new file mode 100644 index 0000000..e0e9f3c --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistMedia.kt @@ -0,0 +1,18 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +abstract class WatchlistMedia( + @SerializedName("poster_path") val posterPath: String?, + @SerializedName("popularity") val popularity: Float, + @SerializedName("id") val id: Int, + @SerializedName("backdrop_path") val backdropPath: String?, + @SerializedName("vote_average") val voteAverage: Float, + @SerializedName("overview") val overview: String, + @SerializedName("release_date", alternate = ["first_air_date"]) val releaseDate: String, + @SerializedName("genre_ids") val genreIds: List, + @SerializedName("original_language") val originalLanguage: String, + @SerializedName("vote_count") val voteCount: Int, + @SerializedName("title", alternate = ["name"]) val title: String, + @SerializedName("original_title", alternate = ["original_name"]) val originalTitle: String +) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistMovie.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistMovie.kt new file mode 100644 index 0000000..926b3eb --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistMovie.kt @@ -0,0 +1,23 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class WatchlistMovie( + posterPath: String?, + popularity: Float, + id: Int, + backdropPath: String?, + voteAverage: Float, + overview: String, + releaseDate: String, + genreIds: List, + originalLanguage: String, + voteCount: Int, + title: String, + originalTitle: String, + @SerializedName("adult") val isAdult: Boolean, + @SerializedName("video") val isVideo: Boolean, +): WatchlistMedia( + posterPath, popularity, id, backdropPath, voteAverage, overview, releaseDate, + genreIds, originalLanguage, voteCount, title, originalTitle +) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistResponse.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistResponse.kt new file mode 100644 index 0000000..53e75f8 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistResponse.kt @@ -0,0 +1,10 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class WatchlistResponse( + @SerializedName("page") val page: Int, + @SerializedName("results") val results: List, + @SerializedName("total_pages") val totalPages: Int, + @SerializedName("total_results") val totalResults: Int +) diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistTvSeries.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistTvSeries.kt new file mode 100644 index 0000000..1974b37 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/WatchlistTvSeries.kt @@ -0,0 +1,22 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +class WatchlistTvSeries( + posterPath: String?, + popularity: Float, + id: Int, + backdropPath: String?, + voteAverage: Float, + overview: String, + releaseDate: String, + genreIds: List, + originalLanguage: String, + voteCount: Int, + title: String, + originalTitle: String, + @SerializedName("origin_country") val originCountry: List +): WatchlistMedia( + posterPath, popularity, id, backdropPath, voteAverage, overview, releaseDate, + genreIds, originalLanguage, voteCount, title, originalTitle +) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Menus.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Menus.kt index 2c5a5cb..99cac23 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Menus.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Menus.kt @@ -1,19 +1,26 @@ package com.owenlejeune.tvtime.ui.components import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* -import androidx.compose.material.AlertDialog +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.DropdownMenu +import androidx.compose.material3.Divider import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp -import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.window.Dialog @Composable @@ -36,7 +43,7 @@ fun TopAppBarDropdownMenu( } DropdownMenu( - modifier = Modifier.background(color = MaterialTheme.colorScheme.surfaceVariant), + modifier = Modifier.background(color = MaterialTheme.colorScheme.background), expanded = expanded.value, onDismissRequest = { expanded.value = false } ) { @@ -44,6 +51,59 @@ fun TopAppBarDropdownMenu( } } +@Composable +fun CustomTopAppBarDropdownMenu( + icon: @Composable () -> Unit = {}, + content: @Composable ColumnScope.(expanded: MutableState) -> Unit = {} +) { + val expanded = remember { mutableStateOf(false) } + + Box(modifier = Modifier.wrapContentSize(Alignment.TopEnd)) { + IconButton(onClick = { expanded.value = true }) { + icon() + } + } + + DropdownMenu( + expanded = expanded.value, + onDismissRequest = { expanded.value = false}, + modifier = Modifier + .background(color = MaterialTheme.colorScheme.background) + .shadow(elevation = 0.dp), + offset = DpOffset(16.dp, 0.dp) + ) { + content(this, expanded) + } +} + +@Composable +fun CustomMenuItem( + text: String, + onClick: () -> Unit +) { + Box( + modifier = Modifier + .clip(RoundedCornerShape(30.dp)) + .fillMaxWidth() + .background(color = MaterialTheme.colorScheme.primary) + ) { + Text( + text = text, + color = MaterialTheme.colorScheme.background, + modifier = Modifier + .padding(horizontal = 15.dp, vertical = 10.dp) + .clickable(onClick = onClick) + .fillMaxWidth(), + textAlign = TextAlign.Center + ) + } +} + +@Composable +fun CustomMenuDivider() { + Divider(color = Color.Transparent, modifier = Modifier.padding(vertical = 2.dp)) +} + @Composable fun TopAppBarDialogMenu( icon: @Composable () -> Unit = {}, diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/tabs/bottom/AccountTab.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/tabs/bottom/AccountTab.kt index b34d55f..fcec8ed 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/tabs/bottom/AccountTab.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/tabs/bottom/AccountTab.kt @@ -1,14 +1,11 @@ package com.owenlejeune.tvtime.ui.screens.tabs.bottom import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.DropdownMenuItem import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AccountCircle -import androidx.compose.material3.Divider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -31,9 +28,7 @@ import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.api.tmdb.model.RatedMovie import com.owenlejeune.tvtime.api.tmdb.model.RatedTopLevelMedia import com.owenlejeune.tvtime.api.tmdb.model.RatedTv -import com.owenlejeune.tvtime.ui.components.RoundedLetterImage -import com.owenlejeune.tvtime.ui.components.SignInDialog -import com.owenlejeune.tvtime.ui.components.TopAppBarDropdownMenu +import com.owenlejeune.tvtime.ui.components.* import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem import com.owenlejeune.tvtime.ui.navigation.ListFetchFun import com.owenlejeune.tvtime.ui.navigation.MainNavItem @@ -175,7 +170,7 @@ private fun AccountDropdownMenu( session: SessionManager.Session?, lastSelectedOption: MutableState ) { - TopAppBarDropdownMenu( + CustomTopAppBarDropdownMenu( icon = { when(session?.isAuthorized) { true -> { } @@ -198,14 +193,14 @@ private fun NoSessionMenuItems( lastSelectedOption: MutableState ) { val showSignInDialog = remember { mutableStateOf(false) } - DropdownMenuItem( + CustomMenuItem( + text = stringResource(id = R.string.action_sign_in), onClick = { showSignInDialog.value = true - }, - modifier = Modifier.background(color = MaterialTheme.colorScheme.surfaceVariant) - ) { - Text(text = stringResource(R.string.action_sign_in), color = MaterialTheme.colorScheme.onSurfaceVariant) - } + } + ) + + CustomMenuDivider() if (showSignInDialog.value) { SignInDialog(showDialog = showSignInDialog) { success -> @@ -216,16 +211,13 @@ private fun NoSessionMenuItems( } } - Divider(color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(horizontal = 12.dp)) - - DropdownMenuItem( + CustomMenuItem( + text = stringResource(R.string.action_sign_in_as_guest), onClick = { createGuestSession(lastSelectedOption) expanded.value = false } - ) { - Text(text = stringResource(R.string.action_sign_in_as_guest), color = MaterialTheme.colorScheme.onSurfaceVariant) - } + ) } @Composable @@ -246,14 +238,10 @@ private fun GuestSessionMenuItems( lastSelectedOption: MutableState ) { val showSignInDialog = remember { mutableStateOf(false) } - DropdownMenuItem( - onClick = { - showSignInDialog.value = true - }, - modifier = Modifier.background(color = MaterialTheme.colorScheme.surfaceVariant) - ) { - Text(text = stringResource(id = R.string.action_sign_in), color = MaterialTheme.colorScheme.onSurfaceVariant) - } + CustomMenuItem( + text = stringResource(id = R.string.action_sign_in), + onClick = { showSignInDialog.value = true } + ) if (showSignInDialog.value) { SignInDialog(showDialog = showSignInDialog) { success -> @@ -264,16 +252,15 @@ private fun GuestSessionMenuItems( } } - Divider(color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(horizontal = 12.dp)) + CustomMenuDivider() - DropdownMenuItem( + CustomMenuItem( + text = stringResource(id = R.string.action_sign_out), onClick = { signOut(lastSelectedOption) expanded.value = false } - ) { - Text(text = stringResource(id = R.string.action_sign_out), color = MaterialTheme.colorScheme.onSurfaceVariant) - } + ) } @Composable @@ -287,14 +274,13 @@ private fun AuthorizedSessionMenuItems( expanded: MutableState, lastSelectedOption: MutableState ) { - DropdownMenuItem( + CustomMenuItem( + text = stringResource(id = R.string.action_sign_out), onClick = { lastSelectedOption.value = ACCOUNT_SIGN_OUT expanded.value = false } - ) { - Text(text = stringResource(id = R.string.action_sign_out), color = MaterialTheme.colorScheme.onSurfaceVariant) - } + ) } private fun createGuestSession(lastSelectedOption: MutableState) {