mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-23 04:00:53 -05:00
custom dropdown menu + setup account api service
This commit is contained in:
@@ -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<AccountDetails>
|
||||||
|
|
||||||
|
@GET("account/{id}/lists")
|
||||||
|
suspend fun getLists(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Query("page") page: Int
|
||||||
|
): Response<AccountListResponse>
|
||||||
|
|
||||||
|
@GET("account/{id}/favorite/movies")
|
||||||
|
suspend fun getFavoriteMovies(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Query("page") page: Int
|
||||||
|
): Response<FavoriteMediaResponse<FavoriteMovie>>
|
||||||
|
|
||||||
|
@GET("account/{id}/favorite/tv")
|
||||||
|
suspend fun getFavoriteTvShows(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Query("page") page: Int
|
||||||
|
): Response<FavoriteMediaResponse<FavoriteTvSeries>>
|
||||||
|
|
||||||
|
// @Headers("Content-Type: application/json;charset=utf-8")
|
||||||
|
@POST("account/{id}/favorite")
|
||||||
|
suspend fun markAsFavorite(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Body body: MarkAsFavoriteBody
|
||||||
|
): Response<StatusResponse>
|
||||||
|
|
||||||
|
@GET("account/{id}/rated/movies")
|
||||||
|
suspend fun getRatedMovies(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Query("page") page: Int
|
||||||
|
): Response<RatedMediaResponse<RatedMovie>>
|
||||||
|
|
||||||
|
@GET("account/{id}/rated/tv")
|
||||||
|
suspend fun getRatedTvShows(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Query("page") page: Int
|
||||||
|
): Response<RatedMediaResponse<RatedTv>>
|
||||||
|
|
||||||
|
@GET("account/{id}/rated/tv/episodes")
|
||||||
|
suspend fun getRatedTvEpisodes(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Query("page") page: Int
|
||||||
|
): Response<RatedMediaResponse<RatedEpisode>>
|
||||||
|
|
||||||
|
@GET("account/{id}/watchlist/movies")
|
||||||
|
suspend fun getMovieWatchlist(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Query("page") page: Int
|
||||||
|
): Response<WatchlistResponse<WatchlistMovie>>
|
||||||
|
|
||||||
|
@GET("account/{id}/watchlist/tv")
|
||||||
|
suspend fun getTvWatchlist(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Query("page") page: Int
|
||||||
|
): Response<WatchlistResponse<WatchlistTvSeries>>
|
||||||
|
|
||||||
|
// @Headers("Content-Type: application/json;charset=utf-8")
|
||||||
|
@POST("account/{id}/watchlist")
|
||||||
|
suspend fun addToWatchlist(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Body body: WatchlistBody
|
||||||
|
): Response<StatusResponse>
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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<AccountDetails> {
|
||||||
|
return accountService.getAccountDetails()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getFavoriteMovies(accountId: Int, page: Int = 1): Response<FavoriteMediaResponse<FavoriteMovie>> {
|
||||||
|
return accountService.getFavoriteMovies(accountId, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getFavoriteTvShows(accountId: Int, page: Int = 1): Response<FavoriteMediaResponse<FavoriteTvSeries>> {
|
||||||
|
return accountService.getFavoriteTvShows(accountId, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun markAsFavorite(accountId: Int, body: MarkAsFavoriteBody): Response<StatusResponse> {
|
||||||
|
return accountService.markAsFavorite(accountId, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getRatedMovies(accountId: Int, page: Int = 1): Response<RatedMediaResponse<RatedMovie>> {
|
||||||
|
return accountService.getRatedMovies(accountId, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getRatedTvShows(accountId: Int, page: Int = 1): Response<RatedMediaResponse<RatedTv>> {
|
||||||
|
return accountService.getRatedTvShows(accountId, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getRatedTvEpisodes(accountId: Int, page: Int = 1): Response<RatedMediaResponse<RatedEpisode>> {
|
||||||
|
return accountService.getRatedTvEpisodes(accountId, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getMovieWatchlist(accountId: Int, page: Int = 1): Response<WatchlistResponse<WatchlistMovie>> {
|
||||||
|
return accountService.getMovieWatchlist(accountId, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getTvWatchlist(accountId: Int, page: Int = 1): Response<WatchlistResponse<WatchlistTvSeries>> {
|
||||||
|
return accountService.getTvWatchlist(accountId, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun addToWatchlist(accountId: Int, body: WatchlistBody): Response<StatusResponse> {
|
||||||
|
return accountService.addToWatchlist(accountId, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,9 +17,9 @@ interface DetailService {
|
|||||||
|
|
||||||
suspend fun getReviews(id: Int): Response<ReviewResponse>
|
suspend fun getReviews(id: Int): Response<ReviewResponse>
|
||||||
|
|
||||||
suspend fun postRating(id: Int, ratingBody: RatingBody): Response<RatingResponse>
|
suspend fun postRating(id: Int, ratingBody: RatingBody): Response<StatusResponse>
|
||||||
|
|
||||||
suspend fun deleteRating(id: Int): Response<RatingResponse>
|
suspend fun deleteRating(id: Int): Response<StatusResponse>
|
||||||
|
|
||||||
suspend fun getKeywords(id: Int): Response<KeywordsResponse>
|
suspend fun getKeywords(id: Int): Response<KeywordsResponse>
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -47,25 +47,25 @@ interface MoviesApi {
|
|||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
@Query("guest_session_id") guestSessionId: String,
|
@Query("guest_session_id") guestSessionId: String,
|
||||||
@Body ratingBody: RatingBody
|
@Body ratingBody: RatingBody
|
||||||
): Response<RatingResponse>
|
): Response<StatusResponse>
|
||||||
|
|
||||||
@POST("movie/{id}/rating")
|
@POST("movie/{id}/rating")
|
||||||
suspend fun postMovieRatingAsUser(
|
suspend fun postMovieRatingAsUser(
|
||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
@Query("session_id") sessionId: String,
|
@Query("session_id") sessionId: String,
|
||||||
@Body ratingBody: RatingBody
|
@Body ratingBody: RatingBody
|
||||||
): Response<RatingResponse>
|
): Response<StatusResponse>
|
||||||
|
|
||||||
@DELETE("movie/{id}/rating")
|
@DELETE("movie/{id}/rating")
|
||||||
suspend fun deleteMovieReviewAsGuest(
|
suspend fun deleteMovieReviewAsGuest(
|
||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
@Query("guest_session_id") guestSessionId: String
|
@Query("guest_session_id") guestSessionId: String
|
||||||
): Response<RatingResponse>
|
): Response<StatusResponse>
|
||||||
|
|
||||||
@DELETE("movie/{id}/rating")
|
@DELETE("movie/{id}/rating")
|
||||||
suspend fun deleteMovieReviewAsUser(
|
suspend fun deleteMovieReviewAsUser(
|
||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
@Query("session_id") sessionId: String
|
@Query("session_id") sessionId: String
|
||||||
): Response<RatingResponse>
|
): Response<StatusResponse>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ class MoviesService: KoinComponent, DetailService, HomePageService {
|
|||||||
return movieService.getReviews(id)
|
return movieService.getReviews(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun postRating(id: Int, rating: RatingBody): Response<RatingResponse> {
|
override suspend fun postRating(id: Int, rating: RatingBody): Response<StatusResponse> {
|
||||||
val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
|
val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
|
||||||
return if (!session.isAuthorized) {
|
return if (!session.isAuthorized) {
|
||||||
movieService.postMovieRatingAsGuest(id, session.sessionId, rating)
|
movieService.postMovieRatingAsGuest(id, session.sessionId, rating)
|
||||||
@@ -62,7 +62,7 @@ class MoviesService: KoinComponent, DetailService, HomePageService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteRating(id: Int): Response<RatingResponse> {
|
override suspend fun deleteRating(id: Int): Response<StatusResponse> {
|
||||||
val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
|
val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
|
||||||
return if (!session.isAuthorized) {
|
return if (!session.isAuthorized) {
|
||||||
movieService.deleteMovieReviewAsGuest(id, session.sessionId)
|
movieService.deleteMovieReviewAsGuest(id, session.sessionId)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.owenlejeune.tvtime.BuildConfig
|
|||||||
import com.owenlejeune.tvtime.api.Client
|
import com.owenlejeune.tvtime.api.Client
|
||||||
import com.owenlejeune.tvtime.api.QueryParam
|
import com.owenlejeune.tvtime.api.QueryParam
|
||||||
import com.owenlejeune.tvtime.extensions.addQueryParams
|
import com.owenlejeune.tvtime.extensions.addQueryParams
|
||||||
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
@@ -43,6 +44,10 @@ class TmdbClient: KoinComponent {
|
|||||||
return client.create(GuestSessionApi::class.java)
|
return client.create(GuestSessionApi::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createAccountService(): AccountApi {
|
||||||
|
return client.create(AccountApi::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
private inner class TmdbInterceptor: Interceptor {
|
private inner class TmdbInterceptor: Interceptor {
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
val apiParam = QueryParam("api_key", BuildConfig.TMDB_ApiKey)
|
val apiParam = QueryParam("api_key", BuildConfig.TMDB_ApiKey)
|
||||||
@@ -51,7 +56,13 @@ class TmdbClient: KoinComponent {
|
|||||||
val languageCode = "${locale.language}-${locale.region}"
|
val languageCode = "${locale.language}-${locale.region}"
|
||||||
val languageParam = QueryParam("language", languageCode)
|
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)
|
return chain.proceed(request)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,25 +47,25 @@ interface TvApi {
|
|||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
@Query("guest_session_id") guestSessionId: String,
|
@Query("guest_session_id") guestSessionId: String,
|
||||||
@Body ratingBody: RatingBody
|
@Body ratingBody: RatingBody
|
||||||
): Response<RatingResponse>
|
): Response<StatusResponse>
|
||||||
|
|
||||||
@POST("tv/{id}/rating")
|
@POST("tv/{id}/rating")
|
||||||
suspend fun postTvRatingAsUser(
|
suspend fun postTvRatingAsUser(
|
||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
@Query("session_id") sessionId: String,
|
@Query("session_id") sessionId: String,
|
||||||
@Body ratingBody: RatingBody
|
@Body ratingBody: RatingBody
|
||||||
): Response<RatingResponse>
|
): Response<StatusResponse>
|
||||||
|
|
||||||
@DELETE("tv/{id}/rating")
|
@DELETE("tv/{id}/rating")
|
||||||
suspend fun deleteTvReviewAsGuest(
|
suspend fun deleteTvReviewAsGuest(
|
||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
@Query("guest_session_id") guestSessionId: String
|
@Query("guest_session_id") guestSessionId: String
|
||||||
): Response<RatingResponse>
|
): Response<StatusResponse>
|
||||||
|
|
||||||
@DELETE("tv/{id}/rating")
|
@DELETE("tv/{id}/rating")
|
||||||
suspend fun deleteTvReviewAsUser(
|
suspend fun deleteTvReviewAsUser(
|
||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
@Query("session_id") sessionId: String
|
@Query("session_id") sessionId: String
|
||||||
): Response<RatingResponse>
|
): Response<StatusResponse>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ class TvService: KoinComponent, DetailService, HomePageService {
|
|||||||
return service.getReviews(id)
|
return service.getReviews(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun postRating(id: Int, rating: RatingBody): Response<RatingResponse> {
|
override suspend fun postRating(id: Int, rating: RatingBody): Response<StatusResponse> {
|
||||||
val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
|
val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
|
||||||
return if (!session.isAuthorized) {
|
return if (!session.isAuthorized) {
|
||||||
service.postTvRatingAsGuest(id, session.sessionId, rating)
|
service.postTvRatingAsGuest(id, session.sessionId, rating)
|
||||||
@@ -62,7 +62,7 @@ class TvService: KoinComponent, DetailService, HomePageService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteRating(id: Int): Response<RatingResponse> {
|
override suspend fun deleteRating(id: Int): Response<StatusResponse> {
|
||||||
val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
|
val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
|
||||||
return if (!session.isAuthorized) {
|
return if (!session.isAuthorized) {
|
||||||
service.deleteTvReviewAsGuest(id, session.sessionId)
|
service.deleteTvReviewAsGuest(id, session.sessionId)
|
||||||
|
|||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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<AccountList>
|
||||||
|
)
|
||||||
@@ -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<Int>,
|
||||||
|
@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
|
||||||
|
)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.owenlejeune.tvtime.api.tmdb.model
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
class FavoriteMediaResponse<T: FavoriteMedia>(
|
||||||
|
@SerializedName("page") val page: Int,
|
||||||
|
@SerializedName("results") val results: List<T>,
|
||||||
|
@SerializedName("total_pages") val totalPages: Int,
|
||||||
|
@SerializedName("total_results") val totalResults: Int
|
||||||
|
)
|
||||||
@@ -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<Int>,
|
||||||
|
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
|
||||||
|
)
|
||||||
@@ -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<Int>,
|
||||||
|
originalLanguage: String,
|
||||||
|
voteCount: Int,
|
||||||
|
title: String,
|
||||||
|
originalTitle: String,
|
||||||
|
@SerializedName("origin_country") val originCountry: List<String>
|
||||||
|
): FavoriteMedia(
|
||||||
|
posterPath, popularity, id, backdropPath, voteAverage, overview, releaseDate,
|
||||||
|
genreIds, originalLanguage, voteCount, title, originalTitle
|
||||||
|
)
|
||||||
@@ -2,7 +2,7 @@ package com.owenlejeune.tvtime.api.tmdb.model
|
|||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
class RatingResponse(
|
class StatusResponse(
|
||||||
@SerializedName("status_code") val statusCode: Int,
|
@SerializedName("status_code") val statusCode: Int,
|
||||||
@SerializedName("status_message") val statusMessage: String
|
@SerializedName("status_message") val statusMessage: String
|
||||||
)
|
)
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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<Int>,
|
||||||
|
@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
|
||||||
|
)
|
||||||
@@ -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<Int>,
|
||||||
|
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
|
||||||
|
)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.owenlejeune.tvtime.api.tmdb.model
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
class WatchlistResponse<T: WatchlistMedia>(
|
||||||
|
@SerializedName("page") val page: Int,
|
||||||
|
@SerializedName("results") val results: List<T>,
|
||||||
|
@SerializedName("total_pages") val totalPages: Int,
|
||||||
|
@SerializedName("total_results") val totalResults: Int
|
||||||
|
)
|
||||||
@@ -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<Int>,
|
||||||
|
originalLanguage: String,
|
||||||
|
voteCount: Int,
|
||||||
|
title: String,
|
||||||
|
originalTitle: String,
|
||||||
|
@SerializedName("origin_country") val originCountry: List<String>
|
||||||
|
): WatchlistMedia(
|
||||||
|
posterPath, popularity, id, backdropPath, voteAverage, overview, releaseDate,
|
||||||
|
genreIds, originalLanguage, voteCount, title, originalTitle
|
||||||
|
)
|
||||||
@@ -1,19 +1,26 @@
|
|||||||
package com.owenlejeune.tvtime.ui.components
|
package com.owenlejeune.tvtime.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.AlertDialog
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.DropdownMenu
|
import androidx.compose.material.DropdownMenu
|
||||||
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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.unit.dp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -36,7 +43,7 @@ fun TopAppBarDropdownMenu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
modifier = Modifier.background(color = MaterialTheme.colorScheme.surfaceVariant),
|
modifier = Modifier.background(color = MaterialTheme.colorScheme.background),
|
||||||
expanded = expanded.value,
|
expanded = expanded.value,
|
||||||
onDismissRequest = { expanded.value = false }
|
onDismissRequest = { expanded.value = false }
|
||||||
) {
|
) {
|
||||||
@@ -44,6 +51,59 @@ fun TopAppBarDropdownMenu(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CustomTopAppBarDropdownMenu(
|
||||||
|
icon: @Composable () -> Unit = {},
|
||||||
|
content: @Composable ColumnScope.(expanded: MutableState<Boolean>) -> 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
|
@Composable
|
||||||
fun TopAppBarDialogMenu(
|
fun TopAppBarDialogMenu(
|
||||||
icon: @Composable () -> Unit = {},
|
icon: @Composable () -> Unit = {},
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.tabs.bottom
|
package com.owenlejeune.tvtime.ui.screens.tabs.bottom
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.DropdownMenuItem
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.AccountCircle
|
import androidx.compose.material.icons.filled.AccountCircle
|
||||||
import androidx.compose.material3.Divider
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
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.RatedMovie
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.RatedTopLevelMedia
|
import com.owenlejeune.tvtime.api.tmdb.model.RatedTopLevelMedia
|
||||||
import com.owenlejeune.tvtime.api.tmdb.model.RatedTv
|
import com.owenlejeune.tvtime.api.tmdb.model.RatedTv
|
||||||
import com.owenlejeune.tvtime.ui.components.RoundedLetterImage
|
import com.owenlejeune.tvtime.ui.components.*
|
||||||
import com.owenlejeune.tvtime.ui.components.SignInDialog
|
|
||||||
import com.owenlejeune.tvtime.ui.components.TopAppBarDropdownMenu
|
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem
|
||||||
import com.owenlejeune.tvtime.ui.navigation.ListFetchFun
|
import com.owenlejeune.tvtime.ui.navigation.ListFetchFun
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
||||||
@@ -175,7 +170,7 @@ private fun AccountDropdownMenu(
|
|||||||
session: SessionManager.Session?,
|
session: SessionManager.Session?,
|
||||||
lastSelectedOption: MutableState<String>
|
lastSelectedOption: MutableState<String>
|
||||||
) {
|
) {
|
||||||
TopAppBarDropdownMenu(
|
CustomTopAppBarDropdownMenu(
|
||||||
icon = {
|
icon = {
|
||||||
when(session?.isAuthorized) {
|
when(session?.isAuthorized) {
|
||||||
true -> { }
|
true -> { }
|
||||||
@@ -198,14 +193,14 @@ private fun NoSessionMenuItems(
|
|||||||
lastSelectedOption: MutableState<String>
|
lastSelectedOption: MutableState<String>
|
||||||
) {
|
) {
|
||||||
val showSignInDialog = remember { mutableStateOf(false) }
|
val showSignInDialog = remember { mutableStateOf(false) }
|
||||||
DropdownMenuItem(
|
CustomMenuItem(
|
||||||
|
text = stringResource(id = R.string.action_sign_in),
|
||||||
onClick = {
|
onClick = {
|
||||||
showSignInDialog.value = true
|
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) {
|
if (showSignInDialog.value) {
|
||||||
SignInDialog(showDialog = showSignInDialog) { success ->
|
SignInDialog(showDialog = showSignInDialog) { success ->
|
||||||
@@ -216,16 +211,13 @@ private fun NoSessionMenuItems(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider(color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(horizontal = 12.dp))
|
CustomMenuItem(
|
||||||
|
text = stringResource(R.string.action_sign_in_as_guest),
|
||||||
DropdownMenuItem(
|
|
||||||
onClick = {
|
onClick = {
|
||||||
createGuestSession(lastSelectedOption)
|
createGuestSession(lastSelectedOption)
|
||||||
expanded.value = false
|
expanded.value = false
|
||||||
}
|
}
|
||||||
) {
|
)
|
||||||
Text(text = stringResource(R.string.action_sign_in_as_guest), color = MaterialTheme.colorScheme.onSurfaceVariant)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -246,14 +238,10 @@ private fun GuestSessionMenuItems(
|
|||||||
lastSelectedOption: MutableState<String>
|
lastSelectedOption: MutableState<String>
|
||||||
) {
|
) {
|
||||||
val showSignInDialog = remember { mutableStateOf(false) }
|
val showSignInDialog = remember { mutableStateOf(false) }
|
||||||
DropdownMenuItem(
|
CustomMenuItem(
|
||||||
onClick = {
|
text = stringResource(id = R.string.action_sign_in),
|
||||||
showSignInDialog.value = true
|
onClick = { showSignInDialog.value = true }
|
||||||
},
|
)
|
||||||
modifier = Modifier.background(color = MaterialTheme.colorScheme.surfaceVariant)
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(id = R.string.action_sign_in), color = MaterialTheme.colorScheme.onSurfaceVariant)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showSignInDialog.value) {
|
if (showSignInDialog.value) {
|
||||||
SignInDialog(showDialog = showSignInDialog) { success ->
|
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 = {
|
onClick = {
|
||||||
signOut(lastSelectedOption)
|
signOut(lastSelectedOption)
|
||||||
expanded.value = false
|
expanded.value = false
|
||||||
}
|
}
|
||||||
) {
|
)
|
||||||
Text(text = stringResource(id = R.string.action_sign_out), color = MaterialTheme.colorScheme.onSurfaceVariant)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -287,14 +274,13 @@ private fun AuthorizedSessionMenuItems(
|
|||||||
expanded: MutableState<Boolean>,
|
expanded: MutableState<Boolean>,
|
||||||
lastSelectedOption: MutableState<String>
|
lastSelectedOption: MutableState<String>
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem(
|
CustomMenuItem(
|
||||||
|
text = stringResource(id = R.string.action_sign_out),
|
||||||
onClick = {
|
onClick = {
|
||||||
lastSelectedOption.value = ACCOUNT_SIGN_OUT
|
lastSelectedOption.value = ACCOUNT_SIGN_OUT
|
||||||
expanded.value = false
|
expanded.value = false
|
||||||
}
|
}
|
||||||
) {
|
)
|
||||||
Text(text = stringResource(id = R.string.action_sign_out), color = MaterialTheme.colorScheme.onSurfaceVariant)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createGuestSession(lastSelectedOption: MutableState<String>) {
|
private fun createGuestSession(lastSelectedOption: MutableState<String>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user