From 5fdb23ce422481a85bc96ca1663f709e83822b7f Mon Sep 17 00:00:00 2001 From: Owen LeJeune Date: Thu, 1 Jun 2023 00:35:29 -0400 Subject: [PATCH] fix some sign in issues --- .../com/owenlejeune/tvtime/MainActivity.kt | 35 +++-- .../owenlejeune/tvtime/api/tmdb/TmdbClient.kt | 14 +- .../tvtime/api/tmdb/api/v3/MoviesApi.kt | 13 -- .../tvtime/api/tmdb/api/v3/MoviesService.kt | 27 ++-- .../tvtime/api/tmdb/api/v3/TvApi.kt | 13 -- .../tvtime/api/tmdb/api/v3/TvService.kt | 29 ++-- .../tvtime/preferences/AppPreferences.kt | 16 -- .../tvtime/ui/navigation/AccountTabNavItem.kt | 26 ++-- .../tvtime/ui/navigation/BottomNavItem.kt | 8 +- .../tvtime/ui/navigation/Routes.kt | 17 +- .../tvtime/ui/screens/main/AccountTab.kt | 145 ++++++------------ .../tvtime/ui/screens/main/ListDetailView.kt | 14 +- .../tvtime/ui/screens/main/MediaDetailView.kt | 24 +-- .../tvtime/ui/screens/main/SettingsTab.kt | 22 +-- .../tvtime/utils/SessionManager.kt | 66 ++++---- 15 files changed, 192 insertions(+), 277 deletions(-) diff --git a/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt b/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt index 5abcf1b..ba62ec6 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt @@ -1,7 +1,6 @@ package com.owenlejeune.tvtime import android.os.Bundle -import android.widget.Toast import androidx.activity.compose.setContent import androidx.compose.animation.rememberSplineBasedDecay import androidx.compose.foundation.layout.* @@ -59,7 +58,7 @@ class MainActivity : MonetCompatActivity() { var mainNavStartRoute = BottomNavItem.SortedItems[0].route intent.data?.let { when (it.host) { - getString(R.string.intent_route_auth_return) -> mainNavStartRoute = BottomNavItem.Account.route + getString(R.string.intent_route_auth_return) -> mainNavStartRoute = BottomNavItem.SIGN_IN_PART_2_ROUTE } } @@ -163,8 +162,8 @@ class MainActivity : MonetCompatActivity() { scrolledContainerColor = MaterialTheme.colorScheme.background ), actions = { - appBarActions.value(this) defaultAppBarActions() + appBarActions.value(this) } ) } @@ -180,6 +179,7 @@ class MainActivity : MonetCompatActivity() { NavigationBar { BottomNavItem.SortedItems.forEach { item -> + val isSelected = currentRoute == item.route || item.alternateRoutes.contains(currentRoute ?: "") NavigationBarItem( modifier = Modifier .padding(4.dp) @@ -189,13 +189,15 @@ class MainActivity : MonetCompatActivity() { val name = if (preferences.showBottomTabLabels) item.name else " " Text(text = name) }, - selected = currentRoute == item.route, + selected = isSelected, onClick = { - onBottomAppBarItemClicked( - navController = navController, - appBarTitle = appBarTitle, - item = item - ) + if (!isSelected) { + onBottomAppBarItemClicked( + navController = navController, + appBarTitle = appBarTitle, + item = item + ) + } } ) } @@ -293,16 +295,19 @@ class MainActivity : MonetCompatActivity() { NavigationRail { Spacer(modifier = Modifier.weight(1f)) BottomNavItem.SortedItems.forEachIndexed { index, item -> + val isSelected = currentRoute == item.route || item.alternateRoutes.contains(currentRoute ?: "") NavigationRailItem( icon = { Icon(painter = painterResource(id = item.icon), contentDescription = null) }, label = { if (preferences.showBottomTabLabels) Text(item.name) }, - selected = currentRoute == item.route, + selected = isSelected, onClick = { - onBottomAppBarItemClicked( - navController = navController, - appBarTitle = appBarTitle, - item = item - ) + if (!isSelected) { + onBottomAppBarItemClicked( + navController = navController, + appBarTitle = appBarTitle, + item = item + ) + } } ) if (index < BottomNavItem.SortedItems.size - 1) { 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 b66c4ac..0e9c879 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 @@ -107,20 +107,16 @@ class TmdbClient: KoinComponent { private fun sessionIdParam(urlSegments: List): QueryParam? { var sessionIdParam: QueryParam? = null if (urlSegments.size > 1 && urlSegments[1] == "account") { - if (SessionManager.currentSession?.isAuthorized == true) { - sessionIdParam = QueryParam("session_id", SessionManager.currentSession!!.sessionId) - } else if (preferences.authorizedSessionId.isNotEmpty()) { - sessionIdParam = QueryParam("session_id", preferences.authorizedSessionId) - } else if (preferences.authorizedSessionValues != null) { - val sessionId = preferences.authorizedSessionValues!!.sessionId - sessionIdParam = QueryParam("session_id", sessionId) + val currentSession = SessionManager.currentSession.value + if (!currentSession?.sessionId.isNullOrEmpty()) { + sessionIdParam = QueryParam("session_id", currentSession!!.sessionId) } } return sessionIdParam } private fun shouldIncludeLanguageParam(urlSegments: List): Boolean { - val ignoredRoutes = listOf("images") + val ignoredRoutes = listOf("images", "account") for (route in ignoredRoutes) { if (urlSegments.contains(route)) { return false @@ -147,7 +143,7 @@ class TmdbClient: KoinComponent { if (url.encodedPathSegments.contains("auth")) { builder.header("Authorization", "Bearer ${BuildConfig.TMDB_Api_v4Key}") } else { - builder.header("Authorization", "Bearer ${SessionManager.currentSession!!.accessToken}") + builder.header("Authorization", "Bearer ${SessionManager.currentSession.value!!.accessToken}") } if (shouldIncludeLanguageParam(url.encodedPathSegments)) { val locale = Locale.current diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/MoviesApi.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/MoviesApi.kt index 80f72ef..8a9760a 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/MoviesApi.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/MoviesApi.kt @@ -42,13 +42,6 @@ interface MoviesApi { @GET("movie/{id}/keywords") suspend fun getKeywords(@Path("id") id: Int): Response - @POST("movie/{id}/rating") - suspend fun postMovieRatingAsGuest( - @Path("id") id: Int, - @Query("guest_session_id") guestSessionId: String, - @Body ratingBody: RatingBody - ): Response - @POST("movie/{id}/rating") suspend fun postMovieRatingAsUser( @Path("id") id: Int, @@ -56,12 +49,6 @@ interface MoviesApi { @Body ratingBody: RatingBody ): Response - @DELETE("movie/{id}/rating") - suspend fun deleteMovieReviewAsGuest( - @Path("id") id: Int, - @Query("guest_session_id") guestSessionId: String - ): Response - @DELETE("movie/{id}/rating") suspend fun deleteMovieReviewAsUser( @Path("id") id: Int, diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/MoviesService.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/MoviesService.kt index b9ec268..3cdb072 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/MoviesService.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/MoviesService.kt @@ -1,7 +1,16 @@ package com.owenlejeune.tvtime.api.tmdb.api.v3 import com.owenlejeune.tvtime.api.tmdb.TmdbClient -import com.owenlejeune.tvtime.api.tmdb.api.v3.model.* +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.CastAndCrew +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailedItem +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePageResponse +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KeywordsResponse +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.MovieReleaseResults +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RatingBody +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ReviewResponse +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.StatusResponse +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.VideoResponse import com.owenlejeune.tvtime.utils.SessionManager import org.koin.core.component.KoinComponent import retrofit2.Response @@ -55,21 +64,13 @@ class MoviesService: KoinComponent, DetailService, HomePageService { } 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) - } else { - movieService.postMovieRatingAsUser(id, session.sessionId, rating) - } + val session = SessionManager.currentSession.value ?: throw Exception("Session must not be null") + return movieService.postMovieRatingAsUser(id, session.sessionId, rating) } 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) - } else { - movieService.deleteMovieReviewAsUser(id, session.sessionId) - } + val session = SessionManager.currentSession.value ?: throw Exception("Session must not be null") + return movieService.deleteMovieReviewAsUser(id, session.sessionId) } override suspend fun getKeywords(id: Int): Response { diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/TvApi.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/TvApi.kt index f8e0653..c3a19e4 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/TvApi.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/TvApi.kt @@ -42,13 +42,6 @@ interface TvApi { @GET("tv/{id}/keywords") suspend fun getKeywords(@Path("id") id: Int): Response - @POST("tv/{id}/rating") - suspend fun postTvRatingAsGuest( - @Path("id") id: Int, - @Query("guest_session_id") guestSessionId: String, - @Body ratingBody: RatingBody - ): Response - @POST("tv/{id}/rating") suspend fun postTvRatingAsUser( @Path("id") id: Int, @@ -56,12 +49,6 @@ interface TvApi { @Body ratingBody: RatingBody ): Response - @DELETE("tv/{id}/rating") - suspend fun deleteTvReviewAsGuest( - @Path("id") id: Int, - @Query("guest_session_id") guestSessionId: String - ): Response - @DELETE("tv/{id}/rating") suspend fun deleteTvReviewAsUser( @Path("id") id: Int, diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/TvService.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/TvService.kt index 114b609..c2ffb85 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/TvService.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v3/TvService.kt @@ -1,7 +1,16 @@ package com.owenlejeune.tvtime.api.tmdb.api.v3 import com.owenlejeune.tvtime.api.tmdb.TmdbClient -import com.owenlejeune.tvtime.api.tmdb.api.v3.model.* +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.CastAndCrew +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailedItem +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePageResponse +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KeywordsResponse +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RatingBody +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ReviewResponse +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.StatusResponse +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TvContentRatings +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.VideoResponse import com.owenlejeune.tvtime.utils.SessionManager import org.koin.core.component.KoinComponent import retrofit2.Response @@ -54,22 +63,14 @@ class TvService: KoinComponent, DetailService, HomePageService { return service.getReviews(id) } - 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) - } else { - service.postTvRatingAsUser(id, session.sessionId, rating) - } + override suspend fun postRating(id: Int, ratingBody: RatingBody): Response { + val session = SessionManager.currentSession.value ?: throw Exception("Session must not be null") + return service.postTvRatingAsUser(id, session.sessionId, ratingBody) } 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) - } else { - service.deleteTvReviewAsUser(id, session.sessionId) - } + val session = SessionManager.currentSession.value ?: throw Exception("Session must not be null") + return service.deleteTvReviewAsUser(id, session.sessionId) } override suspend fun getKeywords(id: Int): Response { diff --git a/app/src/main/java/com/owenlejeune/tvtime/preferences/AppPreferences.kt b/app/src/main/java/com/owenlejeune/tvtime/preferences/AppPreferences.kt index 9458752..b15dd81 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/preferences/AppPreferences.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/preferences/AppPreferences.kt @@ -18,15 +18,12 @@ class AppPreferences(context: Context) { private val PREF_FILE = "tvtime_shared_preferences" private val PERSISTENT_SEARCH = "persistent_search" - private val GUEST_SESSION = "guest_session_id" - private val AUTHORIZED_SESSION = "authorized_session_id" private val AUTHORIZED_SESSION_VALUES = "authorized_session_values" private val FIRST_LAUNCH = "first_launch" private val FIRST_LAUNCH_TESTING = "first_launch_testing" private val CHROMA_MULTIPLIER = "chroma_multiplier" private val USE_SYSTEM_COLORS = "use_system_colors" private val SELECTED_COLOR = "selected_color" - private val USE_V4_API = "use_v4_api" private val SHOW_BACKDROP_GALLERY = "show_backdrop_gallery" private val USE_WALLPAPER_COLORS = "use_wallpaper_colors" private val DARK_THEME = "dark_theme" @@ -79,20 +76,12 @@ class AppPreferences(context: Context) { set(value) { preferences.put(SELECTED_COLOR, value) } /******* Session Tokens ********/ - var guestSessionId: String - get() = preferences.getString(GUEST_SESSION, "") ?: "" - set(value) { preferences.put(GUEST_SESSION, value) } - var authorizedSessionValues: SessionManager.AuthorizedSessionValues? get() = preferences.getString(AUTHORIZED_SESSION_VALUES, null)?.let { Gson().fromJson(it, SessionManager.AuthorizedSessionValues::class.java) } set(value) { preferences.putNullableString(AUTHORIZED_SESSION_VALUES, value?.let { Gson().toJson(value) }) } - var authorizedSessionId: String - get() = preferences.getString(AUTHORIZED_SESSION, "") ?: "" - set(value) { preferences.put(AUTHORIZED_SESSION, value) } - /******** Home Screen Preferences ********/ val moviesTabPositionDefault: Int = 0 var moviesTabPosition: Int @@ -134,11 +123,6 @@ class AppPreferences(context: Context) { get() = preferences.getBoolean(FIRST_LAUNCH, true) set(value) { preferences.put(FIRST_LAUNCH, value) } - val useV4ApiDefault: Boolean = true - var useV4Api: Boolean - get() = preferences.getBoolean(USE_V4_API, useV4ApiDefault) - set(value) { preferences.put(USE_V4_API, value) } - val showBackdropGalleryDefault: Boolean = true var showBackdropGallery: Boolean get() = preferences.getBoolean(SHOW_BACKDROP_GALLERY, showBackdropGalleryDefault) diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/AccountTabNavItem.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/AccountTabNavItem.kt index 1191846..1a58c7b 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/AccountTabNavItem.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/AccountTabNavItem.kt @@ -3,10 +3,16 @@ package com.owenlejeune.tvtime.ui.navigation import androidx.compose.runtime.Composable import androidx.navigation.NavHostController import com.owenlejeune.tvtime.R -import com.owenlejeune.tvtime.api.tmdb.api.v3.model.* +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.FavoriteMovie +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.FavoriteTvSeries +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RatedEpisode +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RatedMovie +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RatedTv +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchlistMovie +import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchlistTvSeries import com.owenlejeune.tvtime.api.tmdb.api.v4.model.V4AccountList -import com.owenlejeune.tvtime.ui.screens.main.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.AccountTabContent +import com.owenlejeune.tvtime.ui.screens.main.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.RecommendedAccountTabContent import com.owenlejeune.tvtime.utils.ResourceUtils import com.owenlejeune.tvtime.utils.SessionManager @@ -45,7 +51,7 @@ sealed class AccountTabNavItem( R.string.no_rated_movies, MediaViewType.MOVIE, screenContent, - { SessionManager.currentSession?.ratedMovies ?: emptyList() }, + { SessionManager.currentSession.value?.ratedMovies ?: emptyList() }, RatedMovie::class, 0 ) @@ -55,7 +61,7 @@ sealed class AccountTabNavItem( R.string.no_rated_tv, MediaViewType.TV, screenContent, - { SessionManager.currentSession?.ratedTvShows ?: emptyList() }, + { SessionManager.currentSession.value?.ratedTvShows ?: emptyList() }, RatedTv::class, 1 ) @@ -65,7 +71,7 @@ sealed class AccountTabNavItem( R.string.no_rated_episodes, MediaViewType.EPISODE, screenContent, - { SessionManager.currentSession?.ratedTvEpisodes ?: emptyList() }, + { SessionManager.currentSession.value?.ratedTvEpisodes ?: emptyList() }, RatedEpisode::class, -1 //2 ) @@ -75,7 +81,7 @@ sealed class AccountTabNavItem( R.string.no_favorite_movies, MediaViewType.MOVIE, screenContent, - { SessionManager.currentSession?.favoriteMovies ?: emptyList() }, + { SessionManager.currentSession.value?.favoriteMovies ?: emptyList() }, FavoriteMovie::class, 3 ) @@ -85,7 +91,7 @@ sealed class AccountTabNavItem( R.string.no_favorite_tv, MediaViewType.TV, screenContent, - { SessionManager.currentSession?.favoriteTvShows ?: emptyList() }, + { SessionManager.currentSession.value?.favoriteTvShows ?: emptyList() }, FavoriteTvSeries::class, 4 ) @@ -95,7 +101,7 @@ sealed class AccountTabNavItem( R.string.no_watchlist_movies, MediaViewType.MOVIE, screenContent, - { SessionManager.currentSession?.movieWatchlist ?: emptyList() }, + { SessionManager.currentSession.value?.movieWatchlist ?: emptyList() }, WatchlistMovie::class, 5 ) @@ -105,7 +111,7 @@ sealed class AccountTabNavItem( R.string.no_watchlist_tv, MediaViewType.TV, screenContent, - { SessionManager.currentSession?.tvWatchlist ?: emptyList() }, + { SessionManager.currentSession.value?.tvWatchlist ?: emptyList() }, WatchlistTvSeries::class, 6 ) @@ -116,7 +122,7 @@ sealed class AccountTabNavItem( R.string.no_lists, MediaViewType.LIST, screenContent, - { SessionManager.currentSession?.accountLists ?: emptyList() }, + { SessionManager.currentSession.value?.accountLists ?: emptyList() }, V4AccountList::class, 7 ) diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/BottomNavItem.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/BottomNavItem.kt index 35fac55..c810252 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/BottomNavItem.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/BottomNavItem.kt @@ -11,7 +11,8 @@ sealed class BottomNavItem( val icon: Int, val route: String, private val orderGetter: (AppPreferences) -> Int, - private val orderSetter: (AppPreferences, Int) -> Unit + private val orderSetter: (AppPreferences, Int) -> Unit, + val alternateRoutes: List = emptyList() ): KoinComponent { private val appPreferences: AppPreferences by inject() @@ -23,6 +24,9 @@ sealed class BottomNavItem( set(value) { orderSetter.invoke(appPreferences, value) } companion object { + + const val SIGN_IN_PART_2_ROUTE = "sign_in_part_two_route" + val SortedItems get() = Items.filter { it.order > -1 }.sortedBy { it.order }.ifEmpty { Items } @@ -42,7 +46,7 @@ sealed class BottomNavItem( object Movies: BottomNavItem(R.string.nav_movies_title, R.drawable.ic_movie, "movies_route", { it.moviesTabPosition }, { p, i -> p.moviesTabPosition = i } ) object TV: BottomNavItem(R.string.nav_tv_title, R.drawable.ic_tv, "tv_route", { it.tvTabPosition }, { p, i -> p.tvTabPosition = i } ) - object Account: BottomNavItem(R.string.nav_account_title, R.drawable.ic_person, "account_route", { it.accountTabPosition }, { p, i -> p.accountTabPosition = i } ) + object Account: BottomNavItem(R.string.nav_account_title, R.drawable.ic_person, "account_route", { it.accountTabPosition }, { p, i -> p.accountTabPosition = i }, listOf(SIGN_IN_PART_2_ROUTE) ) object People: BottomNavItem(R.string.nav_people_title, R.drawable.ic_face, "people_route", { it.peopleTabPosition }, { p, i -> p.peopleTabPosition = i } ) } diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt index a80d837..d786549 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt @@ -6,8 +6,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.navigation.NavHostController +import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable +import androidx.navigation.navArgument import com.owenlejeune.tvtime.ui.screens.main.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.* @@ -46,7 +48,20 @@ fun MainNavGraph( ) } composable(BottomNavItem.Account.route) { - AccountTab(appBarTitle = appBarTitle, appNavController = appNavController, appBarActions = appBarActions) + AccountTab( + appBarTitle = appBarTitle, + appNavController = appNavController, + appBarActions = appBarActions + ) + fab.value = {} + } + composable(BottomNavItem.SIGN_IN_PART_2_ROUTE) { + AccountTab( + appBarTitle = appBarTitle, + appNavController = appNavController, + appBarActions = appBarActions, + doSignInPartTwo = true + ) fab.value = {} } composable(BottomNavItem.People.route) { diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/AccountTab.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/AccountTab.kt index 999ee6f..f7d632b 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/AccountTab.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/AccountTab.kt @@ -31,7 +31,6 @@ import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.api.tmdb.api.v3.model.* import com.owenlejeune.tvtime.api.tmdb.api.v4.model.V4AccountList import com.owenlejeune.tvtime.extensions.unlessEmpty -import com.owenlejeune.tvtime.preferences.AppPreferences import com.owenlejeune.tvtime.ui.components.PagingPosterGrid import com.owenlejeune.tvtime.ui.components.RoundedLetterImage import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem @@ -44,78 +43,54 @@ import com.owenlejeune.tvtime.utils.TmdbUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import org.koin.java.KoinJavaComponent.get import kotlin.reflect.KClass -private const val GUEST_SIGN_IN = "guest_sign_in" -private const val SIGN_OUT = "sign_out" -private const val NO_SESSION_SIGN_IN = "no_session_sign_in" -private const val NO_SESSION_SIGN_IN_GUEST = "no_session_sign_in_guest" - @OptIn(ExperimentalPagerApi::class) @Composable fun AccountTab( appNavController: NavHostController, appBarTitle: MutableState, - appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}) + appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}), + doSignInPartTwo: Boolean = false ) { - val lastSelectedOption = remember { mutableStateOf("") } + val currentSessionState = remember { SessionManager.currentSession } + val currentSession = currentSessionState.value - val lso = lastSelectedOption.value - if (SessionManager.isV4SignInInProgress) { + if (currentSession?.isAuthorized == false) { appBarTitle.value = stringResource(id = R.string.account_not_logged_in) - AccountLoadingView() - v4SignInPart2(lastSelectedOption) + if (doSignInPartTwo) { + AccountLoadingView() + signInPart2() + } } else { - when (SessionManager.currentSession?.isAuthorized) { - false -> { - appBarTitle.value = - stringResource( - id = R.string.account_header_title_formatted, - stringResource(id = R.string.account_name_guest) - ) - } - true -> { - appBarTitle.value = - stringResource( - id = R.string.account_header_title_formatted, - getAccountName(SessionManager.currentSession?.accountDetails) - ) - } - else -> { - appBarTitle.value = stringResource(id = R.string.account_not_logged_in) - } + if (currentSession?.isAuthorized == true) { + appBarTitle.value = + stringResource( + id = R.string.account_header_title_formatted, + getAccountName(currentSession.accountDetails) + ) + } else { + appBarTitle.value = stringResource(id = R.string.account_not_logged_in) } appBarActions.value = { AccountDropdownMenu( - session = SessionManager.currentSession, - lastSelectedOption = lastSelectedOption + session = currentSession ) } - if (!SessionManager.isV4SignInInProgress) { - SessionManager.currentSession?.let { session -> - val tabs = if (session.isAuthorized) { - AccountTabNavItem.AuthorizedItems - } else { - AccountTabNavItem.GuestItems - } + currentSession?.let { + Column { + AuthorizedSessionIcon() - Column { - if (session.isAuthorized) { - AuthorizedSessionIcon() - } - - val pagerState = rememberPagerState() - ScrollableTabs(tabs = tabs, pagerState = pagerState) - AccountTabs( - appNavController = appNavController, - tabs = tabs, - pagerState = pagerState - ) - } + val tabs = AccountTabNavItem.AuthorizedItems + val pagerState = rememberPagerState() + ScrollableTabs(tabs = tabs, pagerState = pagerState) + AccountTabs( + appNavController = appNavController, + tabs = tabs, + pagerState = pagerState + ) } } } @@ -309,10 +284,7 @@ private fun MediaItemRow( } @Composable -private fun AccountDropdownMenu( - session: SessionManager.Session?, - lastSelectedOption: MutableState -) { +private fun AccountDropdownMenu(session: SessionManager.Session?) { val expanded = remember { mutableStateOf(false) } IconButton( @@ -325,73 +297,52 @@ private fun AccountDropdownMenu( expanded = expanded.value, onDismissRequest = { expanded.value = false } ) { - when(session?.isAuthorized) { - true -> { AuthorizedSessionMenuItems(expanded = expanded, lastSelectedOption = lastSelectedOption) } -// false -> { GuestSessionMenuItems(expanded = expanded, lastSelectedOption = lastSelectedOption) } - false -> {} - null -> { NoSessionMenuItems(expanded = expanded, lastSelectedOption = lastSelectedOption) } + if(session?.isAuthorized == true) { + AuthorizedSessionMenuItems(expanded = expanded) + } else { + NoSessionMenuItems(expanded = expanded) } } } @Composable -private fun AuthorizedSessionMenuItems( - expanded: MutableState, - lastSelectedOption: MutableState, - preferences: AppPreferences = get(AppPreferences::class.java) -) { +private fun AuthorizedSessionMenuItems(expanded: MutableState) { DropdownMenuItem( text = { Text(text = stringResource(id = R.string.action_sign_out)) }, onClick = { - if (preferences.useV4Api) { - signOutV4(lastSelectedOption) - } else { -// signOut(lastSelectedOption) - } + signOut() expanded.value = false } ) } @Composable -private fun NoSessionMenuItems( - expanded: MutableState, - lastSelectedOption: MutableState, - preferences: AppPreferences = get(AppPreferences::class.java) -) { +private fun NoSessionMenuItems(expanded: MutableState) { val context = LocalContext.current DropdownMenuItem( text = { Text(text = stringResource(id = R.string.action_sign_in)) }, onClick = { - if (preferences.useV4Api) { - v4SignInPart1(context) - } else { -// showSignInDialog.value = true - } + signInPart1(context) + expanded.value = false } ) } -private fun v4SignInPart1(context: Context) { +private fun signInPart1(context: Context) { CoroutineScope(Dispatchers.IO).launch { - SessionManager.signInWithV4Part1(context) + SessionManager.signInPart1(context) } } -private fun v4SignInPart2(lastSelectedOption: MutableState) { +private fun signInPart2() { CoroutineScope(Dispatchers.IO).launch { - val signIn = SessionManager.signInWithV4Part2() - if (signIn) { - withContext(Dispatchers.Main) { - lastSelectedOption.value = NO_SESSION_SIGN_IN - } - } + SessionManager.singInPart2() } } @Composable private fun AuthorizedSessionIcon() { - val accountDetails = SessionManager.currentSession?.accountDetails + val accountDetails = SessionManager.currentSession.value?.accountDetails val avatarUrl = accountDetails?.let { when { accountDetails.avatar.tmdb?.avatarPath?.isNotEmpty() == true -> { @@ -423,13 +374,9 @@ private fun AuthorizedSessionIcon() { } } -private fun signOutV4(lastSelectedOption: MutableState) { +private fun signOut() { CoroutineScope(Dispatchers.IO).launch { - SessionManager.clearSessionV4 { isSuccessful -> - if (isSuccessful) { - lastSelectedOption.value = SIGN_OUT - } - } + SessionManager.clearSession() } } diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/ListDetailView.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/ListDetailView.kt index f425b8d..38aad0a 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/ListDetailView.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/ListDetailView.kt @@ -544,7 +544,7 @@ private fun ListItemView( @Composable private fun ActionButtonRow(listItem: ListItem) { - val session = SessionManager.currentSession + val session = SessionManager.currentSession.value val (isFavourited, isWatchlisted, isRated) = if (listItem.mediaType == MediaViewType.MOVIE) { Triple( @@ -606,11 +606,12 @@ private fun addToWatchlist( itemIsWatchlisted: MutableState, onWatchlistChanged: (Boolean) -> Unit ) { - val accountId = SessionManager.currentSession!!.accountDetails!!.id + val currentSession = SessionManager.currentSession.value + val accountId = currentSession!!.accountDetails!!.id CoroutineScope(Dispatchers.IO).launch { val response = AccountService().addToWatchlist(accountId, WatchlistBody(type, itemId, !itemIsWatchlisted.value)) if (response.isSuccessful) { - SessionManager.currentSession?.refresh(changed = SessionManager.Session.Changed.Watchlist) + currentSession.refresh(changed = SessionManager.Session.Changed.Watchlist) withContext(Dispatchers.Main) { itemIsWatchlisted.value = !itemIsWatchlisted.value onWatchlistChanged(itemIsWatchlisted.value) @@ -630,11 +631,12 @@ private fun addToFavorite( itemIsFavorited: MutableState, onFavoriteChanged: (Boolean) -> Unit ) { - val accountId = SessionManager.currentSession!!.accountDetails!!.id + val currentSession = SessionManager.currentSession.value + val accountId = currentSession!!.accountDetails!!.id CoroutineScope(Dispatchers.IO).launch { val response = AccountService().markAsFavorite(accountId, MarkAsFavoriteBody(type, itemId, !itemIsFavorited.value)) if (response.isSuccessful) { - SessionManager.currentSession?.refresh(changed = SessionManager.Session.Changed.Favorites) + currentSession.refresh(changed = SessionManager.Session.Changed.Favorites) withContext(Dispatchers.Main) { itemIsFavorited.value = !itemIsFavorited.value onFavoriteChanged(itemIsFavorited.value) @@ -682,7 +684,7 @@ private fun removeItemFromList( val removeItem = DeleteListItemsItem(itemId, itemType) val result = service.deleteListItems(listId, DeleteListItemsBody(listOf(removeItem))) if (result.isSuccessful) { - SessionManager.currentSession?.refresh(SessionManager.Session.Changed.List) + SessionManager.currentSession.value?.refresh(SessionManager.Session.Changed.List) service.getList(listId).body()?.let { withContext(Dispatchers.Main) { list.value = it diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/MediaDetailView.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/MediaDetailView.kt index 71b4754..46e628c 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/MediaDetailView.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/MediaDetailView.kt @@ -267,7 +267,7 @@ private fun ActionsView( modifier: Modifier = Modifier ) { itemId?.let { - val session = SessionManager.currentSession + val session = SessionManager.currentSession.value Row( modifier = modifier .wrapContentSize(), @@ -359,7 +359,7 @@ private fun RateButton( service: DetailService, modifier: Modifier = Modifier ) { - val session = SessionManager.currentSession + val session = SessionManager.currentSession.value val context = LocalContext.current val itemIsRated = remember { @@ -432,7 +432,7 @@ fun WatchlistButton( type: MediaViewType, modifier: Modifier = Modifier ) { - val session = SessionManager.currentSession + val session = SessionManager.currentSession.value val hasWatchlistedItem = if (type == MediaViewType.MOVIE) { session?.hasWatchlistedMovie(itemId) == true @@ -492,7 +492,7 @@ fun FavoriteButton( type: MediaViewType, modifier: Modifier = Modifier ) { - val session = SessionManager.currentSession + val session = SessionManager.currentSession.value val isFavourited = if (type == MediaViewType.MOVIE) { session?.hasFavoritedMovie(itemId) == true } else { @@ -993,7 +993,7 @@ private fun ReviewsCard( color = MaterialTheme.colorScheme.onSurfaceVariant ) - if (SessionManager.currentSession?.isAuthorized == true) { + if (SessionManager.currentSession.value?.isAuthorized == true) { Row( modifier = Modifier .fillMaxWidth() @@ -1230,7 +1230,7 @@ private fun postRating(context: Context, rating: Float, itemId: Int, service: De CoroutineScope(Dispatchers.IO).launch { val response = service.postRating(itemId, RatingBody(rating = rating)) if (response.isSuccessful) { - SessionManager.currentSession?.refresh(changed = arrayOf(SessionManager.Session.Changed.RatedMovies, SessionManager.Session.Changed.RatedTv)) + SessionManager.currentSession.value?.refresh(changed = arrayOf(SessionManager.Session.Changed.RatedMovies, SessionManager.Session.Changed.RatedTv)) withContext(Dispatchers.Main) { itemIsRated.value = true } @@ -1247,7 +1247,7 @@ private fun deleteRating(context: Context, itemId: Int, service: DetailService, CoroutineScope(Dispatchers.IO).launch { val response = service.deleteRating(itemId) if (response.isSuccessful) { - SessionManager.currentSession?.refresh(changed = SessionManager.Session.Changed.Rated) + SessionManager.currentSession.value?.refresh(changed = SessionManager.Session.Changed.Rated) withContext(Dispatchers.Main) { itemIsRated.value = false } @@ -1267,11 +1267,12 @@ private fun addToWatchlist( itemIsWatchlisted: MutableState, onWatchlistChanged: (Boolean) -> Unit ) { - val accountId = SessionManager.currentSession!!.accountDetails!!.id + val currentSession = SessionManager.currentSession.value + val accountId = currentSession!!.accountDetails!!.id CoroutineScope(Dispatchers.IO).launch { val response = AccountService().addToWatchlist(accountId, WatchlistBody(type, itemId, !itemIsWatchlisted.value)) if (response.isSuccessful) { - SessionManager.currentSession?.refresh(changed = SessionManager.Session.Changed.Watchlist) + currentSession.refresh(changed = SessionManager.Session.Changed.Watchlist) withContext(Dispatchers.Main) { itemIsWatchlisted.value = !itemIsWatchlisted.value onWatchlistChanged(itemIsWatchlisted.value) @@ -1291,11 +1292,12 @@ private fun addToFavorite( itemIsFavorited: MutableState, onFavoriteChanged: (Boolean) -> Unit ) { - val accountId = SessionManager.currentSession!!.accountDetails!!.id + val currentSession = SessionManager.currentSession.value + val accountId = currentSession!!.accountDetails!!.id CoroutineScope(Dispatchers.IO).launch { val response = AccountService().markAsFavorite(accountId, MarkAsFavoriteBody(type, itemId, !itemIsFavorited.value)) if (response.isSuccessful) { - SessionManager.currentSession?.refresh(changed = SessionManager.Session.Changed.Favorites) + currentSession.refresh(changed = SessionManager.Session.Changed.Favorites) withContext(Dispatchers.Main) { itemIsFavorited.value = !itemIsFavorited.value onFavoriteChanged(itemIsFavorited.value) diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/SettingsTab.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/SettingsTab.kt index 550d3b5..2d00f4f 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/SettingsTab.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/SettingsTab.kt @@ -480,31 +480,12 @@ private fun DevPreferences( .padding(horizontal = 8.dp, vertical = 12.dp) .clickable( onClick = { - preferences.guestSessionId = "" coroutineScope.launch { - SessionManager.clearSessionV4 { - Toast - .makeText( - context, - "Cleared session v4: $it", - Toast.LENGTH_SHORT - ) - .show() - } + SessionManager.clearSession() } } ) ) - - val useV4Api = remember { mutableStateOf(preferences.useV4Api) } - SwitchPreference( - titleText = "Use v4 API", - checkState = useV4Api.value, - onCheckedChange = { isChecked -> - useV4Api.value = isChecked - preferences.useV4Api = isChecked - } - ) } } @@ -613,7 +594,6 @@ private fun resetDarkModePreferences(preferences: AppPreferences) { private fun resetDevModePreference(preferences: AppPreferences) { preferences.firstLaunchTesting = preferences.firstLaunchTestingDefault - preferences.useV4Api = preferences.useV4ApiDefault preferences.showBackdropGallery = preferences.showBackdropGalleryDefault } diff --git a/app/src/main/java/com/owenlejeune/tvtime/utils/SessionManager.kt b/app/src/main/java/com/owenlejeune/tvtime/utils/SessionManager.kt index 70a8ea6..8f4f687 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/utils/SessionManager.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/utils/SessionManager.kt @@ -3,6 +3,8 @@ package com.owenlejeune.tvtime.utils import android.content.Context import android.content.Intent import android.net.Uri +import android.widget.Toast +import androidx.compose.runtime.mutableStateOf import com.google.gson.annotations.SerializedName import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.api.tmdb.TmdbClient @@ -22,16 +24,13 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.core.component.KoinComponent import org.koin.core.component.inject +import org.koin.java.KoinJavaComponent.get object SessionManager: KoinComponent { private val preferences: AppPreferences by inject() - private var _currentSession: Session? = null - val currentSession: Session? - get() = _currentSession - - var isV4SignInInProgress: Boolean = false + val currentSession = mutableStateOf(null) private val authenticationService by lazy { TmdbClient().createAuthenticationService() } private val authenticationV4Service by lazy { TmdbClient().createV4AuthenticationService() } @@ -42,17 +41,15 @@ object SessionManager: KoinComponent { @SerializedName("account_id") val accountId: String ) - fun clearSessionV4(onResponse: (isSuccessful: Boolean) -> Unit) { - currentSession?.let { session -> + fun clearSession() { + currentSession.value?.let { session -> CoroutineScope(Dispatchers.IO).launch { val deleteResponse = authenticationV4Service.deleteAccessToken(AuthDeleteBody(session.accessToken)) withContext(Dispatchers.Main) { if (deleteResponse.isSuccessful) { - _currentSession = null - preferences.guestSessionId = "" + currentSession.value = null preferences.authorizedSessionValues = null } - onResponse(deleteResponse.isSuccessful) } } } @@ -65,19 +62,17 @@ object SessionManager: KoinComponent { accessToken = values.accessToken, accountId = values.accountId ) - _currentSession = session + currentSession.value = session session.initialize() } } - suspend fun signInWithV4Part1(context: Context) { - isV4SignInInProgress = true - + suspend fun signInPart1(context: Context) { val service = AuthenticationV4Service() val requestTokenResponse = service.createRequestToken(AuthRequestBody(redirect = "app://tvtime.auth.return")) if (requestTokenResponse.isSuccessful) { requestTokenResponse.body()?.let { ctr -> - _currentSession = InProgressSession(ctr.requestToken) + currentSession.value = InProgressSession(ctr.requestToken) val browserIntent = Intent( Intent.ACTION_VIEW, Uri.parse( @@ -89,9 +84,11 @@ object SessionManager: KoinComponent { } } - suspend fun signInWithV4Part2(): Boolean { - if (isV4SignInInProgress && _currentSession is InProgressSession) { - val requestToken = _currentSession!!.sessionId + suspend fun singInPart2( + context: Context = get(Context::class.java) + ) { + if (currentSession.value is InProgressSession) { + val requestToken = currentSession.value!!.sessionId val authResponse = authenticationV4Service.createAccessToken(AuthAccessBody(requestToken)) if (authResponse.isSuccessful) { authResponse.body()?.let { ar -> @@ -104,23 +101,26 @@ object SessionManager: KoinComponent { accountId = ar.accountId, accessToken = ar.accessToken ) - preferences.authorizedSessionId = "" - preferences.guestSessionId = "" - _currentSession = AuthorizedSession( + val session = AuthorizedSession( sessionId = sr.sessionId, accessToken = ar.accessToken, accountId = ar.accountId ) - _currentSession?.initialize() - isV4SignInInProgress = false - return true + currentSession.value = session + session.initialize() } } + } else { + currentSession.value = null + Toast.makeText( + context, + "Error signing in", + Toast.LENGTH_SHORT + ).show() } } } } - return false } abstract class Session(val sessionId: String, val isAuthorized: Boolean, val accessToken: String = "", val accountId: String = "") { @@ -217,7 +217,7 @@ object SessionManager: KoinComponent { val Rated get() = arrayOf(RatedMovies, RatedTv, RatedEpisodes) val Favorites get() = arrayOf(FavoriteMovies, FavoriteTv) val Watchlist get() = arrayOf(WatchlistMovies, WatchlistTv) - val List get() = arrayOf(Changed.Lists) + val List get() = arrayOf(Lists) } } } @@ -234,7 +234,7 @@ object SessionManager: KoinComponent { } private class AuthorizedSession( - sessionId: String = preferences.authorizedSessionId, + sessionId: String = "", accessToken: String = "", accountId: String = "" ): Session(sessionId, true, accessToken, accountId) { @@ -247,12 +247,11 @@ object SessionManager: KoinComponent { override suspend fun refresh(changed: Array) { if (changed.contains(Changed.AccountDetails)) { - service.getAccountDetails().apply { - if (isSuccessful) { - _accountDetails = body() ?: _accountDetails - accountDetails?.let { - refreshWithAccountId(it.id, changed) - } + val response = service.getAccountDetails() + if (response.isSuccessful) { + _accountDetails = response.body() ?: _accountDetails + accountDetails?.let { + refreshWithAccountId(it.id, changed) } } } else if (accountDetails != null) { @@ -335,5 +334,4 @@ object SessionManager: KoinComponent { } } } - } \ No newline at end of file