mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-12-30 11:21:20 -05:00
some work on v4 authentication
This commit is contained in:
@@ -18,12 +18,16 @@
|
|||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/Theme.TVTime"
|
android:theme="@style/Theme.TVTime"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<!-- android:screenOrientation="portrait">-->
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:scheme="app" android:host="@string/intent_route_auth_return" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime
|
package com.owenlejeune.tvtime
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.animation.rememberSplineBasedDecay
|
import androidx.compose.animation.rememberSplineBasedDecay
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
@@ -59,6 +60,13 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
SessionManager.initialize()
|
SessionManager.initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mainNavStartRoute = BottomNavItem.Items[0].route
|
||||||
|
intent.data?.let {
|
||||||
|
when (it.host) {
|
||||||
|
getString(R.string.intent_route_auth_return) -> mainNavStartRoute = BottomNavItem.Account.route
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
monet.awaitMonetReady()
|
monet.awaitMonetReady()
|
||||||
setContent {
|
setContent {
|
||||||
@@ -66,7 +74,7 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
TVTimeTheme(monetCompat = monet) {
|
TVTimeTheme(monetCompat = monet) {
|
||||||
val appNavController = rememberNavController()
|
val appNavController = rememberNavController()
|
||||||
Box {
|
Box {
|
||||||
MainNavigationRoutes(appNavController = appNavController)
|
MainNavigationRoutes(appNavController = appNavController, mainNavStartRoute = mainNavStartRoute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,7 +82,11 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AppScaffold(appNavController: NavHostController, preferences: AppPreferences = get(AppPreferences::class.java)) {
|
private fun AppScaffold(
|
||||||
|
appNavController: NavHostController,
|
||||||
|
mainNavStartRoute: String = BottomNavItem.Items[0].route,
|
||||||
|
preferences: AppPreferences = get(AppPreferences::class.java)
|
||||||
|
) {
|
||||||
val windowSize = rememberWindowSizeClass()
|
val windowSize = rememberWindowSizeClass()
|
||||||
|
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
@@ -127,7 +139,8 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
appBarTitle = appBarTitle,
|
appBarTitle = appBarTitle,
|
||||||
appBarActions = appBarActions,
|
appBarActions = appBarActions,
|
||||||
topBarScrollBehaviour = scrollBehavior
|
topBarScrollBehaviour = scrollBehavior,
|
||||||
|
mainNavStartRoute = mainNavStartRoute
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +193,11 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
item: BottomNavItem
|
item: BottomNavItem
|
||||||
) {
|
) {
|
||||||
appBarTitle.value = item.name
|
appBarTitle.value = item.name
|
||||||
navController.navigate(item.route) {
|
navigateToRoute(navController, item.route)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun navigateToRoute(navController: NavController, route: String) {
|
||||||
|
navController.navigate(route) {
|
||||||
navController.graph.startDestinationRoute?.let { screenRoute ->
|
navController.graph.startDestinationRoute?.let { screenRoute ->
|
||||||
popUpTo(screenRoute) {
|
popUpTo(screenRoute) {
|
||||||
saveState = true
|
saveState = true
|
||||||
@@ -220,7 +237,8 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
||||||
appBarTitle: MutableState<String>,
|
appBarTitle: MutableState<String>,
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({})
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
mainNavStartRoute: String = BottomNavItem.Items[0].route
|
||||||
) {
|
) {
|
||||||
if (windowSize == WindowSizeClass.Expanded) {
|
if (windowSize == WindowSizeClass.Expanded) {
|
||||||
DualColumnMainContent(
|
DualColumnMainContent(
|
||||||
@@ -228,14 +246,16 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
appBarTitle = appBarTitle,
|
appBarTitle = appBarTitle,
|
||||||
appBarActions = appBarActions,
|
appBarActions = appBarActions,
|
||||||
topBarScrollBehaviour = topBarScrollBehaviour
|
topBarScrollBehaviour = topBarScrollBehaviour,
|
||||||
|
mainNavStartRoute = mainNavStartRoute
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
SingleColumnMainContent(
|
SingleColumnMainContent(
|
||||||
appNavController = appNavController,
|
appNavController = appNavController,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
appBarTitle = appBarTitle,
|
appBarTitle = appBarTitle,
|
||||||
appBarActions = appBarActions
|
appBarActions = appBarActions,
|
||||||
|
mainNavStartRoute = mainNavStartRoute
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,13 +265,15 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
appNavController: NavHostController,
|
appNavController: NavHostController,
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
appBarTitle: MutableState<String>,
|
appBarTitle: MutableState<String>,
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({})
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
mainNavStartRoute: String = BottomNavItem.Items[0].route
|
||||||
) {
|
) {
|
||||||
MainMediaView(
|
MainMediaView(
|
||||||
appNavController = appNavController,
|
appNavController = appNavController,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
appBarTitle = appBarTitle,
|
appBarTitle = appBarTitle,
|
||||||
appBarActions = appBarActions
|
appBarActions = appBarActions,
|
||||||
|
mainNavStartRoute = mainNavStartRoute
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +283,8 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
||||||
appBarTitle: MutableState<String>,
|
appBarTitle: MutableState<String>,
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({})
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
mainNavStartRoute: String = BottomNavItem.Items[0].route
|
||||||
) {
|
) {
|
||||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
val currentRoute = navBackStackEntry?.destination?.route
|
val currentRoute = navBackStackEntry?.destination?.route
|
||||||
@@ -295,7 +318,8 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
appNavController = appNavController,
|
appNavController = appNavController,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
appBarTitle = appBarTitle,
|
appBarTitle = appBarTitle,
|
||||||
appBarActions = appBarActions
|
appBarActions = appBarActions,
|
||||||
|
mainNavStartRoute = mainNavStartRoute
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,7 +330,8 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
appNavController: NavHostController,
|
appNavController: NavHostController,
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
appBarTitle: MutableState<String>,
|
appBarTitle: MutableState<String>,
|
||||||
appBarActions: MutableState<RowScope.() -> Unit> = mutableStateOf({})
|
appBarActions: MutableState<RowScope.() -> Unit> = mutableStateOf({}),
|
||||||
|
mainNavStartRoute: String = BottomNavItem.Items[0].route
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
@@ -324,7 +349,8 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
appNavController = appNavController,
|
appNavController = appNavController,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
appBarTitle = appBarTitle,
|
appBarTitle = appBarTitle,
|
||||||
appBarActions = appBarActions
|
appBarActions = appBarActions,
|
||||||
|
startDestination = mainNavStartRoute
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,11 +363,12 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun MainNavigationRoutes(
|
private fun MainNavigationRoutes(
|
||||||
startDestination: String = MainNavItem.MainView.route,
|
startDestination: String = MainNavItem.MainView.route,
|
||||||
|
mainNavStartRoute: String = BottomNavItem.Items[0].route,
|
||||||
appNavController: NavHostController,
|
appNavController: NavHostController,
|
||||||
) {
|
) {
|
||||||
NavHost(navController = appNavController, startDestination = startDestination) {
|
NavHost(navController = appNavController, startDestination = startDestination) {
|
||||||
composable(MainNavItem.MainView.route) {
|
composable(MainNavItem.MainView.route) {
|
||||||
AppScaffold(appNavController = appNavController)
|
AppScaffold(appNavController = appNavController, mainNavStartRoute = mainNavStartRoute)
|
||||||
}
|
}
|
||||||
composable(
|
composable(
|
||||||
MainNavItem.DetailView.route.plus("/{${NavConstants.TYPE_KEY}}/{${NavConstants.ID_KEY}}"),
|
MainNavItem.DetailView.route.plus("/{${NavConstants.TYPE_KEY}}/{${NavConstants.ID_KEY}}"),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.owenlejeune.tvtime
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.facebook.stetho.Stetho
|
import com.facebook.stetho.Stetho
|
||||||
|
import com.kieronquinn.monetcompat.core.MonetCompat
|
||||||
import com.owenlejeune.tvtime.di.modules.appModule
|
import com.owenlejeune.tvtime.di.modules.appModule
|
||||||
import com.owenlejeune.tvtime.di.modules.networkModule
|
import com.owenlejeune.tvtime.di.modules.networkModule
|
||||||
import com.owenlejeune.tvtime.di.modules.preferencesModule
|
import com.owenlejeune.tvtime.di.modules.preferencesModule
|
||||||
@@ -28,6 +29,8 @@ class TvTimeApplication: Application() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MonetCompat.enablePaletteCompat()
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Stetho.initializeWithDefaults(this)
|
Stetho.initializeWithDefaults(this)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,13 @@ class TmdbClient: KoinComponent {
|
|||||||
private inner class V4Interceptor: Interceptor {
|
private inner class V4Interceptor: Interceptor {
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
val builder = chain.request().newBuilder()
|
val builder = chain.request().newBuilder()
|
||||||
builder.header("Authorization", "Bearer ${BuildConfig.TMDB_Api_v4Key}")
|
with(chain.request()) {
|
||||||
|
if (url.encodedPathSegments.contains("auth")) {
|
||||||
|
builder.header("Authorization", "Bearer ${BuildConfig.TMDB_Api_v4Key}")
|
||||||
|
} else {
|
||||||
|
builder.header("Authorization", "Bearer ${SessionManager.currentSession!!.accessToken}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val locale = Locale.current
|
val locale = Locale.current
|
||||||
val languageCode = "${locale.language}-${locale.region}"
|
val languageCode = "${locale.language}-${locale.region}"
|
||||||
|
|||||||
@@ -23,4 +23,7 @@ interface AuthenticationApi {
|
|||||||
|
|
||||||
@POST("authentication/token/validate_with_login")
|
@POST("authentication/token/validate_with_login")
|
||||||
suspend fun validateTokenWithLogin(@Body body: TokenValidationBody): Response<CreateTokenResponse>
|
suspend fun validateTokenWithLogin(@Body body: TokenValidationBody): Response<CreateTokenResponse>
|
||||||
|
|
||||||
|
@POST("authentication/session/convert/4")
|
||||||
|
suspend fun createSessionFromV4Token(@Body body: V4TokenBody): Response<CreateSessionResponse>
|
||||||
}
|
}
|
||||||
@@ -27,4 +27,8 @@ class AuthenticationService {
|
|||||||
suspend fun validateTokenWithLogin(body: TokenValidationBody): Response<CreateTokenResponse> {
|
suspend fun validateTokenWithLogin(body: TokenValidationBody): Response<CreateTokenResponse> {
|
||||||
return service.validateTokenWithLogin(body)
|
return service.validateTokenWithLogin(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun createSessionFromV4Token(body: V4TokenBody): Response<CreateSessionResponse> {
|
||||||
|
return service.createSessionFromV4Token(body)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
class V4TokenBody(
|
||||||
|
@SerializedName("access_token") val accessToken: String
|
||||||
|
)
|
||||||
@@ -59,9 +59,10 @@ fun MainNavGraph(
|
|||||||
appNavController: NavHostController,
|
appNavController: NavHostController,
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
appBarTitle: MutableState<String>,
|
appBarTitle: MutableState<String>,
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({})
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
startDestination: String = BottomNavItem.Items[0].route
|
||||||
) {
|
) {
|
||||||
NavHost(navController = navController, startDestination = BottomNavItem.Movies.route) {
|
NavHost(navController = navController, startDestination = startDestination) {
|
||||||
composable(BottomNavItem.Movies.route) {
|
composable(BottomNavItem.Movies.route) {
|
||||||
appBarActions.value = {}
|
appBarActions.value = {}
|
||||||
MediaTab(appNavController = appNavController, mediaType = MediaViewType.MOVIE)
|
MediaTab(appNavController = appNavController, mediaType = MediaViewType.MOVIE)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.tabs.bottom
|
package com.owenlejeune.tvtime.ui.screens.tabs.bottom
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
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
|
||||||
@@ -7,13 +8,11 @@ import androidx.compose.foundation.shape.CircleShape
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -52,7 +51,13 @@ fun AccountTab(
|
|||||||
appBarTitle: MutableState<String>,
|
appBarTitle: MutableState<String>,
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({})
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({})
|
||||||
) {
|
) {
|
||||||
if (appBarTitle.value.equals(stringResource(id = R.string.nav_account_title))) {
|
val lastSelectedOption = remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
if (SessionManager.isV4SignInInProgress) {
|
||||||
|
v4SignInPart2(lastSelectedOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appBarTitle.value == stringResource(id = R.string.nav_account_title)) {
|
||||||
when (SessionManager.currentSession?.isAuthorized) {
|
when (SessionManager.currentSession?.isAuthorized) {
|
||||||
false -> {
|
false -> {
|
||||||
appBarTitle.value =
|
appBarTitle.value =
|
||||||
@@ -74,34 +79,30 @@ fun AccountTab(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val lastSelectedOption = remember { mutableStateOf("") }
|
|
||||||
|
|
||||||
appBarActions.value = {
|
appBarActions.value = {
|
||||||
AccountDropdownMenu(session = SessionManager.currentSession, lastSelectedOption = lastSelectedOption)
|
AccountDropdownMenu(session = SessionManager.currentSession, lastSelectedOption = lastSelectedOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastSelectedOption.value.isNotBlank() || lastSelectedOption.value.isBlank()) {
|
SessionManager.currentSession?.let { session ->
|
||||||
SessionManager.currentSession?.let { session ->
|
val tabs = if (session.isAuthorized) {
|
||||||
val tabs = if (session.isAuthorized) {
|
AccountTabNavItem.AuthorizedItems
|
||||||
AccountTabNavItem.AuthorizedItems
|
} else {
|
||||||
} else {
|
AccountTabNavItem.GuestItems
|
||||||
AccountTabNavItem.GuestItems
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
when(session.isAuthorized) {
|
||||||
|
true -> { AuthorizedSessionIcon() }
|
||||||
|
false -> { GuestSessionIcon() }
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
val pagerState = rememberPagerState()
|
||||||
when(session.isAuthorized) {
|
ScrollableTabs(tabs = tabs, pagerState = pagerState)
|
||||||
true -> { AuthorizedSessionIcon() }
|
AccountTabs(
|
||||||
false -> { GuestSessionIcon() }
|
appNavController = appNavController,
|
||||||
}
|
tabs = tabs,
|
||||||
|
pagerState = pagerState
|
||||||
val pagerState = rememberPagerState()
|
)
|
||||||
ScrollableTabs(tabs = tabs, pagerState = pagerState)
|
|
||||||
AccountTabs(
|
|
||||||
appNavController = appNavController,
|
|
||||||
tabs = tabs,
|
|
||||||
pagerState = pagerState
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,6 +339,11 @@ private fun NoSessionMenuItems(
|
|||||||
text = { Text(text = stringResource(id = R.string.action_sign_in)) },
|
text = { Text(text = stringResource(id = R.string.action_sign_in)) },
|
||||||
onClick = { showSignInDialog.value = true }
|
onClick = { showSignInDialog.value = true }
|
||||||
)
|
)
|
||||||
|
// val context = LocalContext.current
|
||||||
|
// DropdownMenuItem(
|
||||||
|
// text = { Text(text = stringResource(id = R.string.action_sign_in)) },
|
||||||
|
// onClick = { v4SignInPart1(context) }
|
||||||
|
// )
|
||||||
|
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(text = stringResource(id = R.string.action_sign_in_as_guest)) },
|
text = { Text(text = stringResource(id = R.string.action_sign_in_as_guest)) },
|
||||||
@@ -348,6 +354,21 @@ private fun NoSessionMenuItems(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun v4SignInPart1(context: Context) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
SessionManager.signInWithV4Part1(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun v4SignInPart2(lastSelectedOption: MutableState<String>) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val signIn = SessionManager.signInWithV4Part2()
|
||||||
|
if (signIn) {
|
||||||
|
lastSelectedOption.value = NO_SESSION_SIGN_IN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun GuestSessionIcon() {
|
private fun GuestSessionIcon() {
|
||||||
val guestName = stringResource(id = R.string.account_name_guest)
|
val guestName = stringResource(id = R.string.account_name_guest)
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
package com.owenlejeune.tvtime.utils
|
package com.owenlejeune.tvtime.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.AccountService
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.AccountService
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.AuthenticationService
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.AuthenticationService
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.GuestSessionService
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.GuestSessionService
|
||||||
import com.owenlejeune.tvtime.api.tmdb.TmdbClient
|
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.*
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.AuthenticationV4Service
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AuthAccessBody
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AuthDeleteBody
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AuthRequestBody
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -21,7 +29,10 @@ object SessionManager: KoinComponent {
|
|||||||
val currentSession: Session?
|
val currentSession: Session?
|
||||||
get() = _currentSession
|
get() = _currentSession
|
||||||
|
|
||||||
|
var isV4SignInInProgress: Boolean = false
|
||||||
|
|
||||||
private val authenticationService by lazy { TmdbClient().createAuthenticationService() }
|
private val authenticationService by lazy { TmdbClient().createAuthenticationService() }
|
||||||
|
private val authenticationV4Service by lazy { TmdbClient().createV4AuthenticationService() }
|
||||||
|
|
||||||
fun clearSession(onResponse: (isSuccessful: Boolean) -> Unit) {
|
fun clearSession(onResponse: (isSuccessful: Boolean) -> Unit) {
|
||||||
currentSession?.let { session ->
|
currentSession?.let { session ->
|
||||||
@@ -39,6 +50,22 @@ object SessionManager: KoinComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clearSessionV4(onResponse: (isSuccessful: Boolean) -> Unit) {
|
||||||
|
currentSession?.let { session ->
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val deleteResponse = authenticationV4Service.deleteAccessToken(AuthDeleteBody(session.sessionId))
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (deleteResponse.isSuccessful) {
|
||||||
|
_currentSession = null
|
||||||
|
preferences.guestSessionId = ""
|
||||||
|
preferences.authorizedSessionId = ""
|
||||||
|
}
|
||||||
|
onResponse(deleteResponse.isSuccessful)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun initialize() {
|
suspend fun initialize() {
|
||||||
if (preferences.guestSessionId.isNotEmpty()) {
|
if (preferences.guestSessionId.isNotEmpty()) {
|
||||||
val session = GuestSession()
|
val session = GuestSession()
|
||||||
@@ -91,7 +118,57 @@ object SessionManager: KoinComponent {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class Session(val sessionId: String, val isAuthorized: Boolean) {
|
suspend fun signInWithV4Part1(context: Context) {
|
||||||
|
isV4SignInInProgress = true
|
||||||
|
|
||||||
|
val service = AuthenticationV4Service()
|
||||||
|
val requestTokenResponse = service.createRequestToken(AuthRequestBody(redirect = ""))
|
||||||
|
if (requestTokenResponse.isSuccessful) {
|
||||||
|
requestTokenResponse.body()?.let { ctr ->
|
||||||
|
_currentSession = InProgressSession(ctr.requestToken)
|
||||||
|
val browserIntent = Intent(
|
||||||
|
Intent.ACTION_VIEW,
|
||||||
|
Uri.parse(
|
||||||
|
context.getString(R.string.tmdb_auth_url, ctr.requestToken)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
context.startActivity(browserIntent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun signInWithV4Part2(): Boolean {
|
||||||
|
if (isV4SignInInProgress && _currentSession is InProgressSession) {
|
||||||
|
val requestToken = _currentSession!!.sessionId
|
||||||
|
val authResponse = authenticationV4Service.createAccessToken(AuthAccessBody(requestToken))
|
||||||
|
if (authResponse.isSuccessful) {
|
||||||
|
authResponse.body()?.let { ar ->
|
||||||
|
if (ar.success) {
|
||||||
|
val sessionResponse = authenticationService.createSessionFromV4Token(V4TokenBody(ar.accessToken))
|
||||||
|
if (sessionResponse.isSuccessful) {
|
||||||
|
sessionResponse.body()?.let { sr ->
|
||||||
|
preferences.authorizedSessionId = sr.sessionId
|
||||||
|
preferences.guestSessionId = ""
|
||||||
|
_currentSession = AuthorizedSession(accessToken = ar.accessToken)
|
||||||
|
_currentSession?.initialize()
|
||||||
|
isV4SignInInProgress = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// preferences.authorizedSessionId = ar.accessToken
|
||||||
|
// preferences.guestSessionId = ""
|
||||||
|
// _currentSession = AuthorizedSession()
|
||||||
|
// _currentSession?.initialize()
|
||||||
|
// isV4SignInInProgress = false
|
||||||
|
// return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Session(val sessionId: String, val isAuthorized: Boolean, val accessToken: String = "") {
|
||||||
protected open var _ratedMovies: List<RatedMovie> = emptyList()
|
protected open var _ratedMovies: List<RatedMovie> = emptyList()
|
||||||
val ratedMovies: List<RatedMovie>
|
val ratedMovies: List<RatedMovie>
|
||||||
get() = _ratedMovies
|
get() = _ratedMovies
|
||||||
@@ -187,7 +264,18 @@ object SessionManager: KoinComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AuthorizedSession: Session(preferences.authorizedSessionId, true) {
|
private class InProgressSession(requestToken: String): Session(requestToken, false) {
|
||||||
|
override suspend fun initialize() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun refresh(changed: Array<Changed>) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AuthorizedSession(accessToken: String = ""): Session(preferences.authorizedSessionId, true, accessToken) {
|
||||||
private val service by lazy { AccountService() }
|
private val service by lazy { AccountService() }
|
||||||
|
|
||||||
override suspend fun initialize() {
|
override suspend fun initialize() {
|
||||||
|
|||||||
@@ -100,4 +100,8 @@
|
|||||||
<string name="username_label">Username</string>
|
<string name="username_label">Username</string>
|
||||||
<string name="password_label">Password</string>
|
<string name="password_label">Password</string>
|
||||||
<string name="no_account_message">Don\'t have an account?</string>
|
<string name="no_account_message">Don\'t have an account?</string>
|
||||||
|
<string name="tmdb_auth_url">"https://www.themoviedb.org/auth/access?request_token=%1$s"</string>
|
||||||
|
|
||||||
|
<!-- intent routes -->
|
||||||
|
<string name="intent_route_auth_return">tvtime.auth.return</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user