mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-22 19:50:54 -05:00
move account and sign in/out options to overlay menu
This commit is contained in:
@@ -1,20 +1,18 @@
|
|||||||
package com.owenlejeune.tvtime
|
package com.owenlejeune.tvtime
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.animation.rememberSplineBasedDecay
|
import androidx.compose.animation.rememberSplineBasedDecay
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.Scaffold
|
import androidx.compose.material.Scaffold
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Settings
|
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
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.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.compositeOver
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
@@ -23,7 +21,6 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.NavType
|
import androidx.navigation.NavType
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
@@ -31,11 +28,17 @@ import androidx.navigation.compose.composable
|
|||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
|
import androidx.navigation.navDeepLink
|
||||||
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import com.kieronquinn.monetcompat.app.MonetCompatActivity
|
import com.kieronquinn.monetcompat.app.MonetCompatActivity
|
||||||
import com.owenlejeune.tvtime.extensions.WindowSizeClass
|
import com.owenlejeune.tvtime.extensions.WindowSizeClass
|
||||||
import com.owenlejeune.tvtime.extensions.navigateInBottomBar
|
import com.owenlejeune.tvtime.extensions.navigateInBottomBar
|
||||||
import com.owenlejeune.tvtime.extensions.rememberWindowSizeClass
|
import com.owenlejeune.tvtime.extensions.rememberWindowSizeClass
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
|
import com.owenlejeune.tvtime.ui.components.AccountIcon
|
||||||
|
import com.owenlejeune.tvtime.ui.components.ProfileMenuContainer
|
||||||
|
import com.owenlejeune.tvtime.ui.components.ProfileMenuDefaults
|
||||||
|
import com.owenlejeune.tvtime.ui.components.ProfileMenuOverlay
|
||||||
import com.owenlejeune.tvtime.ui.navigation.BottomNavItem
|
import com.owenlejeune.tvtime.ui.navigation.BottomNavItem
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavGraph
|
import com.owenlejeune.tvtime.ui.navigation.MainNavGraph
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
||||||
@@ -89,8 +92,6 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
preferences: AppPreferences = get(AppPreferences::class.java)
|
preferences: AppPreferences = get(AppPreferences::class.java)
|
||||||
) {
|
) {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
|
||||||
val currentRoute = navBackStackEntry?.destination?.route
|
|
||||||
|
|
||||||
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
|
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
|
||||||
val topAppBarScrollState = rememberTopAppBarScrollState()
|
val topAppBarScrollState = rememberTopAppBarScrollState()
|
||||||
@@ -102,7 +103,24 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
val appBarActions = remember { mutableStateOf<@Composable RowScope.() -> Unit>({}) }
|
val appBarActions = remember { mutableStateOf<@Composable RowScope.() -> Unit>({}) }
|
||||||
val fab = remember { mutableStateOf<@Composable () -> Unit>({}) }
|
val fab = remember { mutableStateOf<@Composable () -> Unit>({}) }
|
||||||
|
|
||||||
Scaffold (
|
val showProfileMenuOverlay = remember { mutableStateOf(false) }
|
||||||
|
val navigationIcon = @Composable {
|
||||||
|
AccountIcon(
|
||||||
|
modifier = Modifier.padding(horizontal = 12.dp),
|
||||||
|
size = 32.dp,
|
||||||
|
onClick = { showProfileMenuOverlay.value = true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val defaultNavBarColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.08f).compositeOver(background = MaterialTheme.colorScheme.surface)
|
||||||
|
|
||||||
|
ProfileMenuContainer(
|
||||||
|
appNavController = appNavController,
|
||||||
|
visible = showProfileMenuOverlay.value,
|
||||||
|
onDismissRequest = { showProfileMenuOverlay.value = false },
|
||||||
|
colors = ProfileMenuDefaults.systemBarColors(navBarColor = defaultNavBarColor)
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
if (windowSize != WindowSizeClass.Expanded) {
|
if (windowSize != WindowSizeClass.Expanded) {
|
||||||
@@ -110,7 +128,8 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
appNavController = appNavController,
|
appNavController = appNavController,
|
||||||
title = appBarTitle.value,
|
title = appBarTitle.value,
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
appBarActions = appBarActions
|
appBarActions = appBarActions,
|
||||||
|
navigationIcon = navigationIcon
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -132,28 +151,22 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
appBarTitle = appBarTitle,
|
appBarTitle = appBarTitle,
|
||||||
appBarActions = appBarActions,
|
appBarActions = appBarActions,
|
||||||
topBarScrollBehaviour = scrollBehavior,
|
topBarScrollBehaviour = scrollBehavior,
|
||||||
mainNavStartRoute = mainNavStartRoute
|
mainNavStartRoute = mainNavStartRoute,
|
||||||
|
navigationIcon = navigationIcon
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TopBar(
|
private fun TopBar(
|
||||||
appNavController: NavHostController,
|
appNavController: NavHostController,
|
||||||
title: @Composable () -> Unit,
|
title: @Composable () -> Unit,
|
||||||
scrollBehavior: TopAppBarScrollBehavior,
|
scrollBehavior: TopAppBarScrollBehavior,
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({})
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
navigationIcon: @Composable () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val defaultAppBarActions: @Composable RowScope.() -> Unit = {
|
|
||||||
IconButton(
|
|
||||||
onClick = {
|
|
||||||
appNavController.navigate(MainNavItem.SettingsView.route)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Icon(imageVector = Icons.Filled.Settings, contentDescription = stringResource(id = R.string.nav_settings_title))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LargeTopAppBar(
|
LargeTopAppBar(
|
||||||
title = title,
|
title = title,
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
@@ -162,9 +175,9 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
scrolledContainerColor = MaterialTheme.colorScheme.background
|
scrolledContainerColor = MaterialTheme.colorScheme.background
|
||||||
),
|
),
|
||||||
actions = {
|
actions = {
|
||||||
defaultAppBarActions()
|
|
||||||
appBarActions.value(this)
|
appBarActions.value(this)
|
||||||
}
|
},
|
||||||
|
navigationIcon = navigationIcon
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +221,7 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
||||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
navigationIcon: @Composable () -> Unit = {},
|
||||||
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route
|
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route
|
||||||
) {
|
) {
|
||||||
if (windowSize == WindowSizeClass.Expanded) {
|
if (windowSize == WindowSizeClass.Expanded) {
|
||||||
@@ -218,7 +232,8 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
appBarTitle = appBarTitle,
|
appBarTitle = appBarTitle,
|
||||||
appBarActions = appBarActions,
|
appBarActions = appBarActions,
|
||||||
topBarScrollBehaviour = topBarScrollBehaviour,
|
topBarScrollBehaviour = topBarScrollBehaviour,
|
||||||
mainNavStartRoute = mainNavStartRoute
|
mainNavStartRoute = mainNavStartRoute,
|
||||||
|
navigationIcon = navigationIcon
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
SingleColumnMainContent(
|
SingleColumnMainContent(
|
||||||
@@ -259,6 +274,7 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
||||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
navigationIcon: @Composable () -> Unit = {},
|
||||||
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route,
|
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route,
|
||||||
preferences: AppPreferences = get(AppPreferences::class.java)
|
preferences: AppPreferences = get(AppPreferences::class.java)
|
||||||
) {
|
) {
|
||||||
@@ -291,7 +307,8 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
appNavController = appNavController,
|
appNavController = appNavController,
|
||||||
title = appBarTitle.value,
|
title = appBarTitle.value,
|
||||||
scrollBehavior = topBarScrollBehaviour,
|
scrollBehavior = topBarScrollBehaviour,
|
||||||
appBarActions = appBarActions
|
appBarActions = appBarActions,
|
||||||
|
navigationIcon = navigationIcon
|
||||||
)
|
)
|
||||||
MainMediaView(
|
MainMediaView(
|
||||||
appNavController = appNavController,
|
appNavController = appNavController,
|
||||||
@@ -435,6 +452,18 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
WebLinkView(url = url, appNavController = appNavController)
|
WebLinkView(url = url, appNavController = appNavController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
composable(
|
||||||
|
route = MainNavItem.AccountView.route,
|
||||||
|
deepLinks = listOf(
|
||||||
|
navDeepLink { uriPattern = "app://tvtime.auth.{${NavConstants.ACCOUNT_KEY}}" }
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
val deepLink = it.arguments?.getString(NavConstants.ACCOUNT_KEY)
|
||||||
|
AccountView(
|
||||||
|
appNavController = appNavController,
|
||||||
|
doSignInPartTwo = deepLink == NavConstants.AUTH_REDIRECT_PAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,141 +1,282 @@
|
|||||||
package com.owenlejeune.tvtime.ui.components
|
package com.owenlejeune.tvtime.ui.components
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.background
|
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.Arrangement
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.material.DropdownMenu
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
import androidx.compose.material3.Divider
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Login
|
||||||
|
import androidx.compose.material.icons.outlined.Logout
|
||||||
|
import androidx.compose.material.icons.outlined.Person
|
||||||
|
import androidx.compose.material.icons.outlined.PersonAdd
|
||||||
|
import androidx.compose.material.icons.outlined.Settings
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
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
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
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.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.compositeOver
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
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.window.Dialog
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
|
import com.owenlejeune.tvtime.BuildConfig
|
||||||
|
import com.owenlejeune.tvtime.R
|
||||||
|
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
||||||
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
private const val ALPHA = 0.7f
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun TopAppBarDropdownMenu(
|
fun ProfileMenuOverlay(
|
||||||
icon: @Composable () -> Unit = {},
|
appNavController: NavController,
|
||||||
content: @Composable ColumnScope.(expanded: MutableState<Boolean>) -> Unit = {}
|
visible: Boolean,
|
||||||
) {
|
onDismissRequest: () -> Unit
|
||||||
val expanded = remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.wrapContentSize(Alignment.TopEnd)
|
|
||||||
) {
|
|
||||||
IconButton(
|
|
||||||
onClick = {
|
|
||||||
expanded.value = true
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
icon()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DropdownMenu(
|
|
||||||
modifier = Modifier.background(color = MaterialTheme.colorScheme.background),
|
|
||||||
expanded = expanded.value,
|
|
||||||
onDismissRequest = { expanded.value = false }
|
|
||||||
) {
|
|
||||||
content(this, expanded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun CustomTopAppBarDropdownMenu(
|
|
||||||
icon: @Composable () -> Unit = {},
|
|
||||||
content: @Composable ColumnScope.(expanded: MutableState<Boolean>) -> Unit = {}
|
|
||||||
) {
|
|
||||||
val expanded = remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Box(modifier = Modifier.wrapContentSize(Alignment.TopEnd).padding(end = 12.dp)) {
|
|
||||||
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
|
|
||||||
) {
|
) {
|
||||||
|
if (visible) {
|
||||||
|
val context = LocalContext.current
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(RoundedCornerShape(30.dp))
|
.fillMaxSize()
|
||||||
|
.background(color = Color.Black.copy(alpha = ALPHA))
|
||||||
|
.clickable(onClick = onDismissRequest)
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.TopCenter)
|
||||||
|
.padding(vertical = 100.dp, horizontal = 12.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(color = MaterialTheme.colorScheme.primary)
|
.wrapContentHeight(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = Color.Black.copy(alpha = 0.6f).compositeOver(MaterialTheme.colorScheme.primaryContainer),
|
||||||
|
contentColor = Color.White.copy(alpha = 0.8f).compositeOver(MaterialTheme.colorScheme.onPrimaryContainer)
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(vertical = 12.dp)
|
||||||
|
) {
|
||||||
|
val currentSessionState = remember { SessionManager.currentSession }
|
||||||
|
val currentSession = currentSessionState.value
|
||||||
|
|
||||||
|
currentSession?.let {
|
||||||
|
ProfileMenuItem {
|
||||||
|
AccountIcon(
|
||||||
|
size = 48.dp,
|
||||||
|
enabled = false
|
||||||
|
)
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = currentSession.accountDetails.value?.name ?: "",
|
||||||
color = MaterialTheme.colorScheme.background,
|
fontSize = 20.sp,
|
||||||
modifier = Modifier
|
fontWeight = FontWeight.Bold
|
||||||
.padding(horizontal = 15.dp, vertical = 10.dp)
|
|
||||||
.clickable(onClick = onClick)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
MenuDivider()
|
||||||
fun CustomMenuDivider() {
|
|
||||||
Divider(color = Color.Transparent, modifier = Modifier.padding(vertical = 2.dp))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
ProfileMenuItem(
|
||||||
fun TopAppBarDialogMenu(
|
|
||||||
icon: @Composable () -> Unit = {},
|
|
||||||
content: @Composable (showing: MutableState<Boolean>) -> Unit = {}
|
|
||||||
) {
|
|
||||||
val expanded = remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.wrapContentSize(Alignment.TopEnd)
|
|
||||||
) {
|
|
||||||
IconButton(
|
|
||||||
onClick = {
|
onClick = {
|
||||||
expanded.value = true
|
Toast.makeText(context, "Under construction", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
icon()
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.PersonAdd,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.account_add),
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
ProfileMenuItem {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.account_not_logged_in),
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expanded.value) {
|
MenuDivider()
|
||||||
Dialog(
|
|
||||||
onDismissRequest = { expanded.value = false },
|
currentSession?.let {
|
||||||
content = { content(expanded) }
|
ProfileMenuItem(
|
||||||
|
onClick = {
|
||||||
|
onDismissRequest()
|
||||||
|
appNavController.navigate(MainNavItem.AccountView.route)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Person,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.nav_account_title),
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileMenuItem(
|
||||||
|
onClick = {
|
||||||
|
onDismissRequest()
|
||||||
|
appNavController.navigate(MainNavItem.SettingsView.route)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Settings,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.nav_settings_title),
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuDivider()
|
||||||
|
|
||||||
|
ProfileMenuItem(
|
||||||
|
onClick = {
|
||||||
|
onDismissRequest()
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
if (currentSession != null) {
|
||||||
|
SessionManager.clearSession()
|
||||||
|
} else {
|
||||||
|
SessionManager.signInPart1(context) {
|
||||||
|
appNavController.navigate(
|
||||||
|
MainNavItem.WebLinkView.route.plus("/$it")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
if (currentSession != null) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Logout,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.action_sign_out),
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Login,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.action_sign_in),
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuDivider()
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "${stringResource(id = R.string.app_name)} v${BuildConfig.VERSION_NAME}",
|
||||||
|
fontSize = 10.sp,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MenuDivider() {
|
||||||
|
MyDivider(modifier = Modifier.padding(vertical = 9.dp))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ProfileMenuItem(
|
||||||
|
onClick: (() -> Unit)? = null,
|
||||||
|
content: @Composable RowScope.() -> Unit
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable(
|
||||||
|
enabled = onClick != null,
|
||||||
|
onClick = onClick ?: {}
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(18.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 24.dp, vertical = 9.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProfileMenuColors internal constructor(val statusBarColor: Color, val navBarColor: Color)
|
||||||
|
|
||||||
|
object ProfileMenuDefaults {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun systemBarColors(
|
||||||
|
statusBarColor: Color = MaterialTheme.colorScheme.background,
|
||||||
|
navBarColor: Color = MaterialTheme.colorScheme.background
|
||||||
|
) = ProfileMenuColors(statusBarColor, navBarColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ProfileMenuContainer(
|
||||||
|
colors: ProfileMenuColors = ProfileMenuDefaults.systemBarColors(),
|
||||||
|
appNavController: NavController,
|
||||||
|
visible: Boolean,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
content: @Composable BoxScope.() -> Unit
|
||||||
|
) {
|
||||||
|
val statusBarColor = if (visible) {
|
||||||
|
Color.Black.copy(alpha = ALPHA).compositeOver(background = colors.statusBarColor)
|
||||||
|
} else {
|
||||||
|
colors.statusBarColor
|
||||||
|
}
|
||||||
|
val navBarColor = if (visible) {
|
||||||
|
Color.Black.copy(alpha = ALPHA).compositeOver(background = colors.navBarColor)
|
||||||
|
} else {
|
||||||
|
colors.navBarColor
|
||||||
|
}
|
||||||
|
|
||||||
|
val systemUiController = rememberSystemUiController()
|
||||||
|
systemUiController.setStatusBarColor(statusBarColor)
|
||||||
|
systemUiController.setNavigationBarColor(navBarColor)
|
||||||
|
|
||||||
|
Box {
|
||||||
|
content()
|
||||||
|
|
||||||
|
ProfileMenuOverlay(
|
||||||
|
visible = visible,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
appNavController = appNavController
|
||||||
)
|
)
|
||||||
// AlertDialog(
|
|
||||||
// modifier = Modifier
|
|
||||||
// .fillMaxWidth()
|
|
||||||
// .wrapContentHeight(),
|
|
||||||
// backgroundColor = MaterialTheme.colorScheme.background,
|
|
||||||
// onDismissRequest = { expanded.value = false },
|
|
||||||
// text = { content(expanded) },
|
|
||||||
// buttons = {}
|
|
||||||
// )
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,13 +247,16 @@ fun PosterItem(
|
|||||||
colors = CardDefaults.cardColors(containerColor = Color.Transparent)
|
colors = CardDefaults.cardColors(containerColor = Color.Transparent)
|
||||||
) {
|
) {
|
||||||
var backgroundColor by remember { mutableStateOf(Color.Gray) }
|
var backgroundColor by remember { mutableStateOf(Color.Gray) }
|
||||||
|
val m = if (backgroundColor == Color.Transparent) {
|
||||||
|
Modifier.wrapContentHeight()
|
||||||
|
} else {
|
||||||
|
Modifier.height(POSTER_HEIGHT)
|
||||||
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = m
|
||||||
.width(width = width)
|
.width(width = width)
|
||||||
.height(height = POSTER_HEIGHT)
|
|
||||||
.background(color = backgroundColor)
|
.background(color = backgroundColor)
|
||||||
.clip(RoundedCornerShape(5.dp))
|
.clip(RoundedCornerShape(5.dp))
|
||||||
.onGloballyPositioned { sizeImage = it.size }
|
|
||||||
) {
|
) {
|
||||||
var bgIcon by remember { mutableStateOf(placeholder) }
|
var bgIcon by remember { mutableStateOf(placeholder) }
|
||||||
Icon(
|
Icon(
|
||||||
@@ -286,7 +289,7 @@ fun PosterItem(
|
|||||||
},
|
},
|
||||||
model = url,
|
model = url,
|
||||||
contentDescription = title,
|
contentDescription = title,
|
||||||
contentScale = ContentScale.FillWidth,
|
contentScale = ContentScale.FillBounds,
|
||||||
onSuccess = { backgroundColor = Color.Transparent }
|
onSuccess = { backgroundColor = Color.Transparent }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ import androidx.compose.foundation.text.BasicTextField
|
|||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
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.ArrowDropDown
|
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||||
import androidx.compose.material.icons.filled.ArrowDropUp
|
import androidx.compose.material.icons.filled.ArrowDropUp
|
||||||
import androidx.compose.material.icons.filled.Error
|
import androidx.compose.material.icons.filled.Error
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.filled.Visibility
|
import androidx.compose.material.icons.filled.Visibility
|
||||||
import androidx.compose.material.icons.filled.VisibilityOff
|
import androidx.compose.material.icons.filled.VisibilityOff
|
||||||
|
import androidx.compose.material.icons.outlined.AccountCircle
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
@@ -65,9 +67,11 @@ import coil.compose.rememberAsyncImagePainter
|
|||||||
import com.google.accompanist.flowlayout.FlowRow
|
import com.google.accompanist.flowlayout.FlowRow
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.AuthorDetails
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.AuthorDetails
|
||||||
|
import com.owenlejeune.tvtime.extensions.unlessEmpty
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
||||||
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -696,34 +700,90 @@ fun AvatarImage(
|
|||||||
contentDescription = ""
|
contentDescription = ""
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val text = if (author.name.isNotEmpty()) author.name[0] else author.username[0]
|
val name = author.name.unlessEmpty(author.username)
|
||||||
RoundedLetterImage(
|
UserInitials(size = size, name = name)
|
||||||
size = size,
|
}
|
||||||
character = text
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UserInitials(
|
||||||
|
size: Dp,
|
||||||
|
name: String,
|
||||||
|
fontSize: TextUnit = 16.sp
|
||||||
|
) {
|
||||||
|
val sanitizedName = name.replace("\\s+".toRegex(), " ")
|
||||||
|
val userName = if(sanitizedName.contains(" ")) {
|
||||||
|
sanitizedName.split(" ")[0][0].toString() + sanitizedName.split(" ")[1][0].toString()
|
||||||
|
} else {
|
||||||
|
if (sanitizedName.length < 3) name else { sanitizedName.substring(0, 2) }
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(CircleShape)
|
||||||
|
.size(size)
|
||||||
|
.background(color = MaterialTheme.colorScheme.secondary)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
text = userName.uppercase(),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
style = TextStyle(
|
||||||
|
color = MaterialTheme.colorScheme.background,
|
||||||
|
fontSize = fontSize
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RoundedLetterImage(
|
fun AccountIcon(
|
||||||
size: Dp,
|
modifier: Modifier = Modifier,
|
||||||
character: Char,
|
size: Dp = 60.dp,
|
||||||
modifier: Modifier = Modifier
|
onClick: () -> Unit = {},
|
||||||
|
enabled: Boolean = true
|
||||||
) {
|
) {
|
||||||
Box(
|
val accountDetails = SessionManager.currentSession.value?.accountDetails?.value
|
||||||
modifier = modifier
|
val avatarUrl = accountDetails?.let {
|
||||||
|
when {
|
||||||
|
accountDetails.avatar.tmdb?.avatarPath?.isNotEmpty() == true -> {
|
||||||
|
TmdbUtils.getAccountAvatarUrl(accountDetails)
|
||||||
|
}
|
||||||
|
accountDetails.avatar.gravatar?.isDefault() == false -> {
|
||||||
|
TmdbUtils.getAccountGravatarUrl(accountDetails)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(modifier = modifier
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.size(size)
|
.clickable(
|
||||||
.background(color = MaterialTheme.colorScheme.tertiary)
|
enabled = enabled,
|
||||||
) {
|
onClick = onClick
|
||||||
Text(
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.Center),
|
|
||||||
text = character.uppercase(),
|
|
||||||
color = MaterialTheme.colorScheme.onTertiary,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
style = MaterialTheme.typography.titleLarge
|
|
||||||
)
|
)
|
||||||
|
) {
|
||||||
|
if (accountDetails == null) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.AccountCircle,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(size),
|
||||||
|
tint = MaterialTheme.colorScheme.secondary
|
||||||
|
)
|
||||||
|
} else if (avatarUrl == null) {
|
||||||
|
val name = accountDetails.name.ifEmpty { accountDetails.username }
|
||||||
|
UserInitials(size = size, name = name)
|
||||||
|
} else {
|
||||||
|
Box(modifier = Modifier.size(size)) {
|
||||||
|
AsyncImage(
|
||||||
|
model = avatarUrl,
|
||||||
|
contentDescription = "",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(60.dp)
|
||||||
|
.clip(CircleShape),
|
||||||
|
contentScale = ContentScale.Fit
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1036,3 +1096,8 @@ fun SearchBar(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MyDivider(modifier: Modifier = Modifier) {
|
||||||
|
Divider(thickness = 0.5.dp, modifier = modifier, color = MaterialTheme.colorScheme.secondaryContainer)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.owenlejeune.tvtime.ui.navigation
|
|||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.utils.ResourceUtils
|
import com.owenlejeune.tvtime.utils.ResourceUtils
|
||||||
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
@@ -41,9 +42,39 @@ 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 Movies: BottomNavItem(
|
||||||
object TV: BottomNavItem(R.string.nav_tv_title, R.drawable.ic_tv, "tv_route", { it.tvTabPosition }, { p, i -> p.tvTabPosition = i } )
|
R.string.nav_movies_title,
|
||||||
object Account: BottomNavItem(R.string.nav_account_title, R.drawable.ic_person, "account_route", { it.accountTabPosition }, { p, i -> p.accountTabPosition = i } )
|
R.drawable.ic_movie,
|
||||||
object People: BottomNavItem(R.string.nav_people_title, R.drawable.ic_face, "people_route", { it.peopleTabPosition }, { p, i -> p.peopleTabPosition = i } )
|
"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",
|
||||||
|
{
|
||||||
|
// if (SessionManager.currentSession.value?.isAuthorized == true) {
|
||||||
|
// it.accountTabPosition
|
||||||
|
// } else {
|
||||||
|
-2
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
{ p, i -> p.accountTabPosition = i }
|
||||||
|
)
|
||||||
|
object People: BottomNavItem(
|
||||||
|
R.string.nav_people_title,
|
||||||
|
R.drawable.ic_face,
|
||||||
|
"people_route",
|
||||||
|
{ it.peopleTabPosition },
|
||||||
|
{ p, i -> p.peopleTabPosition = i }
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,4 +12,6 @@ sealed class MainNavItem(val route: String) {
|
|||||||
object SearchView: MainNavItem("search_route")
|
object SearchView: MainNavItem("search_route")
|
||||||
object WebLinkView: MainNavItem("web_link_route")
|
object WebLinkView: MainNavItem("web_link_route")
|
||||||
|
|
||||||
|
object AccountView: MainNavItem("account_route")
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
package com.owenlejeune.tvtime.ui.navigation
|
package com.owenlejeune.tvtime.ui.navigation
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
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.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.NavType
|
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.navArgument
|
|
||||||
import androidx.navigation.navDeepLink
|
import androidx.navigation.navDeepLink
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.*
|
import com.owenlejeune.tvtime.ui.screens.main.*
|
||||||
@@ -45,19 +42,8 @@ fun MainNavGraph(
|
|||||||
fab = fab
|
fab = fab
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(
|
composable(route = BottomNavItem.Account.route) {
|
||||||
route = BottomNavItem.Account.route,
|
AccountViewContent(appNavController = appNavController)
|
||||||
deepLinks = listOf(
|
|
||||||
navDeepLink { uriPattern = "app://tvtime.auth.{${NavConstants.ACCOUNT_KEY}}" }
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
val deepLink = it.arguments?.getString(NavConstants.ACCOUNT_KEY)
|
|
||||||
AccountTab(
|
|
||||||
appBarTitle = appBarTitle,
|
|
||||||
appNavController = appNavController,
|
|
||||||
appBarActions = appBarActions,
|
|
||||||
doSignInPartTwo = deepLink == NavConstants.AUTH_REDIRECT_PAGE
|
|
||||||
)
|
|
||||||
fab.value = {}
|
fab.value = {}
|
||||||
}
|
}
|
||||||
composable(BottomNavItem.People.route) {
|
composable(BottomNavItem.People.route) {
|
||||||
|
|||||||
@@ -1,29 +1,25 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.screens.main
|
||||||
|
|
||||||
|
import androidx.compose.animation.rememberSplineBasedDecay
|
||||||
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.foundation.shape.CircleShape
|
|
||||||
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.ArrowBack
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
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.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
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.input.nestedscroll.nestedScroll
|
||||||
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
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import com.google.accompanist.pager.HorizontalPager
|
import com.google.accompanist.pager.HorizontalPager
|
||||||
import com.google.accompanist.pager.PagerState
|
import com.google.accompanist.pager.PagerState
|
||||||
@@ -31,63 +27,104 @@ import com.google.accompanist.pager.rememberPagerState
|
|||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
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.model.V4AccountList
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.V4AccountList
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.viewmodel.RecommendedMediaViewModel
|
||||||
import com.owenlejeune.tvtime.extensions.unlessEmpty
|
import com.owenlejeune.tvtime.extensions.unlessEmpty
|
||||||
|
import com.owenlejeune.tvtime.ui.components.AccountIcon
|
||||||
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
||||||
import com.owenlejeune.tvtime.ui.components.RoundedLetterImage
|
import com.owenlejeune.tvtime.ui.components.ProfileMenuContainer
|
||||||
|
import com.owenlejeune.tvtime.ui.components.ProfileMenuOverlay
|
||||||
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
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.tabs.top.ScrollableTabs
|
import com.owenlejeune.tvtime.ui.screens.main.tabs.top.ScrollableTabs
|
||||||
import com.owenlejeune.tvtime.api.tmdb.viewmodel.RecommendedMediaViewModel
|
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountTab(
|
fun AccountView(
|
||||||
appNavController: NavHostController,
|
appNavController: NavHostController,
|
||||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
|
||||||
doSignInPartTwo: Boolean = false
|
doSignInPartTwo: Boolean = false
|
||||||
) {
|
) {
|
||||||
val currentSessionState = remember { SessionManager.currentSession }
|
val currentSessionState = remember { SessionManager.currentSession }
|
||||||
val currentSession = currentSessionState.value
|
val currentSession = currentSessionState.value
|
||||||
|
|
||||||
|
val showProfileMenuOverlay = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
ProfileMenuContainer(
|
||||||
|
appNavController = appNavController,
|
||||||
|
visible = showProfileMenuOverlay.value,
|
||||||
|
onDismissRequest = { showProfileMenuOverlay.value = false }
|
||||||
|
) {
|
||||||
|
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
|
||||||
|
val topAppBarScrollState = rememberTopAppBarScrollState()
|
||||||
|
val scrollBehavior = remember(decayAnimationSpec) {
|
||||||
|
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(decayAnimationSpec, topAppBarScrollState)
|
||||||
|
}
|
||||||
|
Scaffold(
|
||||||
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
topBar = {
|
||||||
|
LargeTopAppBar(
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(
|
||||||
|
onClick = { appNavController.popBackStack() }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.ArrowBack,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
if (currentSession?.isAuthorized == false) {
|
||||||
|
Text(text = stringResource(id = R.string.account_not_logged_in))
|
||||||
|
} else {
|
||||||
|
val accountDetails = remember { currentSession!!.accountDetails }
|
||||||
|
Text(text = getAccountName(accountDetails.value))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors = TopAppBarDefaults.largeTopAppBarColors(scrolledContainerColor = MaterialTheme.colorScheme.background),
|
||||||
|
actions = {
|
||||||
|
AccountIcon(
|
||||||
|
modifier = Modifier.padding(start = 12.dp),
|
||||||
|
size = 32.dp,
|
||||||
|
onClick = { showProfileMenuOverlay.value = true }
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Box(modifier = Modifier.padding(it)) {
|
||||||
|
AccountViewContent(appNavController = appNavController, doSignInPartTwo = doSignInPartTwo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
|
@Composable
|
||||||
|
fun AccountViewContent(
|
||||||
|
appNavController: NavHostController,
|
||||||
|
doSignInPartTwo: Boolean = false
|
||||||
|
) {
|
||||||
|
val currentSessionState = remember { SessionManager.currentSession }
|
||||||
|
val currentSession = currentSessionState.value
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
if (currentSession?.isAuthorized == false) {
|
if (currentSession?.isAuthorized != true && doSignInPartTwo) {
|
||||||
appBarTitle.value = { Text(text = stringResource(id = R.string.account_not_logged_in)) }
|
|
||||||
if (doSignInPartTwo) {
|
|
||||||
AccountLoadingView()
|
AccountLoadingView()
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
SessionManager.singInPart2()
|
SessionManager.signInPart2()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (currentSession?.isAuthorized == true) {
|
|
||||||
val accountDetails = remember { currentSession.accountDetails }
|
|
||||||
appBarTitle.value = { Text(text = stringResource(id = R.string.account_header_title_formatted, getAccountName(accountDetails.value))) }
|
|
||||||
} else {
|
|
||||||
appBarTitle.value = { Text(text = stringResource(id = R.string.account_not_logged_in)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
appBarActions.value = {
|
|
||||||
AccountDropdownMenu(
|
|
||||||
session = currentSession,
|
|
||||||
appNavController = appNavController
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentSession?.let {
|
currentSession?.let {
|
||||||
Column {
|
Column {
|
||||||
AuthorizedSessionIcon()
|
|
||||||
|
|
||||||
val tabs = AccountTabNavItem.AuthorizedItems
|
val tabs = AccountTabNavItem.AuthorizedItems
|
||||||
val pagerState = rememberPagerState()
|
val pagerState = rememberPagerState()
|
||||||
ScrollableTabs(tabs = tabs, pagerState = pagerState)
|
ScrollableTabs(tabs = tabs, pagerState = pagerState)
|
||||||
@@ -288,95 +325,6 @@ private fun MediaItemRow(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun AccountDropdownMenu(session: SessionManager.Session?, appNavController: NavHostController) {
|
|
||||||
val expanded = remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
IconButton(
|
|
||||||
onClick = { expanded.value = true }
|
|
||||||
) {
|
|
||||||
Icon(imageVector = Icons.Filled.AccountCircle, contentDescription = stringResource(id = R.string.nav_account_title))
|
|
||||||
}
|
|
||||||
|
|
||||||
DropdownMenu(
|
|
||||||
expanded = expanded.value,
|
|
||||||
onDismissRequest = { expanded.value = false }
|
|
||||||
) {
|
|
||||||
if(session?.isAuthorized == true) {
|
|
||||||
AuthorizedSessionMenuItems(expanded = expanded)
|
|
||||||
} else {
|
|
||||||
NoSessionMenuItems(expanded = expanded, appNavController = appNavController)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun AuthorizedSessionMenuItems(expanded: MutableState<Boolean>) {
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text(text = stringResource(id = R.string.action_sign_out)) },
|
|
||||||
onClick = {
|
|
||||||
signOut()
|
|
||||||
expanded.value = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun NoSessionMenuItems(expanded: MutableState<Boolean>, appNavController: NavHostController) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text(text = stringResource(id = R.string.action_sign_in)) },
|
|
||||||
onClick = {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
SessionManager.signInPart1(context) {
|
|
||||||
appNavController.navigate(MainNavItem.WebLinkView.route.plus("/$it"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expanded.value = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun AuthorizedSessionIcon() {
|
|
||||||
val accountDetails = SessionManager.currentSession.value?.accountDetails?.value
|
|
||||||
val avatarUrl = accountDetails?.let {
|
|
||||||
when {
|
|
||||||
accountDetails.avatar.tmdb?.avatarPath?.isNotEmpty() == true -> {
|
|
||||||
TmdbUtils.getAccountAvatarUrl(accountDetails)
|
|
||||||
}
|
|
||||||
accountDetails.avatar.gravatar?.isDefault() == false -> {
|
|
||||||
TmdbUtils.getAccountGravatarUrl(accountDetails)
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(modifier = Modifier.padding(start = 12.dp)) {
|
|
||||||
if (accountDetails == null || avatarUrl == null) {
|
|
||||||
val accLetter = (accountDetails?.name?.ifEmpty { accountDetails.username } ?: " ")[0]
|
|
||||||
RoundedLetterImage(size = 60.dp, character = accLetter)
|
|
||||||
} else {
|
|
||||||
Box(modifier = Modifier.size(60.dp)) {
|
|
||||||
AsyncImage(
|
|
||||||
model = avatarUrl,
|
|
||||||
contentDescription = "",
|
|
||||||
modifier = Modifier
|
|
||||||
.size(60.dp)
|
|
||||||
.clip(CircleShape),
|
|
||||||
contentScale = ContentScale.Fit
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun signOut() {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
SessionManager.clearSession()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -2,7 +2,7 @@ package com.owenlejeune.tvtime.ui.screens.main
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.Text
|
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.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -22,7 +22,7 @@ fun PeopleTab(
|
|||||||
appNavController: NavHostController,
|
appNavController: NavHostController,
|
||||||
fab: MutableState<@Composable () -> Unit>
|
fab: MutableState<@Composable () -> Unit>
|
||||||
) {
|
) {
|
||||||
val titleText = stringResource(id = R.string.nav_people_title)
|
val titleText = stringResource(id = R.string.popular_today_header)
|
||||||
appBarTitle.value = { Text(text = titleText) }
|
appBarTitle.value = { Text(text = titleText) }
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -38,10 +38,10 @@ fun PeopleTab(
|
|||||||
PagingPeoplePosterGrid(
|
PagingPeoplePosterGrid(
|
||||||
lazyPagingItems = peopleList,
|
lazyPagingItems = peopleList,
|
||||||
header = {
|
header = {
|
||||||
Text(
|
// Text(
|
||||||
text = stringResource(R.string.popular_today_header),
|
// text = stringResource(R.string.popular_today_header),
|
||||||
modifier = Modifier.padding(start = 8.dp)
|
// modifier = Modifier.padding(start = 8.dp)
|
||||||
)
|
// )
|
||||||
},
|
},
|
||||||
onClick = { id ->
|
onClick = { id ->
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ private fun HomeScreenPreferences(
|
|||||||
SwitchPreference(
|
SwitchPreference(
|
||||||
titleText = stringResource(R.string.preference_show_poster_titles_title),
|
titleText = stringResource(R.string.preference_show_poster_titles_title),
|
||||||
subtitleText = stringResource(R.string.preference_show_poster_titles_subtitle),
|
subtitleText = stringResource(R.string.preference_show_poster_titles_subtitle),
|
||||||
checkState = showTabLabels.value,
|
checkState = showPosterTitles.value,
|
||||||
onCheckedChange = { isChecked ->
|
onCheckedChange = { isChecked ->
|
||||||
showPosterTitles.value = isChecked
|
showPosterTitles.value = isChecked
|
||||||
preferences.showPosterTitles = isChecked
|
preferences.showPosterTitles = isChecked
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import androidx.navigation.NavController
|
|||||||
import com.google.accompanist.web.AccompanistWebViewClient
|
import com.google.accompanist.web.AccompanistWebViewClient
|
||||||
import com.google.accompanist.web.WebView
|
import com.google.accompanist.web.WebView
|
||||||
import com.google.accompanist.web.rememberWebViewState
|
import com.google.accompanist.web.rememberWebViewState
|
||||||
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
@@ -37,7 +38,12 @@ fun WebLinkView(
|
|||||||
title = {},
|
title = {},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { appNavController.popBackStack() }
|
onClick = {
|
||||||
|
if (url.contains("auth")) {
|
||||||
|
SessionManager.cancelSignIn()
|
||||||
|
}
|
||||||
|
appNavController.popBackStack()
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.Close,
|
imageVector = Icons.Filled.Close,
|
||||||
@@ -47,8 +53,8 @@ fun WebLinkView(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) { paddingValues ->
|
||||||
Box(modifier = Modifier.padding(it)) {
|
Box(modifier = Modifier.padding(paddingValues)) {
|
||||||
val webViewState = rememberWebViewState(url = url)
|
val webViewState = rememberWebViewState(url = url)
|
||||||
WebView(
|
WebView(
|
||||||
state = webViewState,
|
state = webViewState,
|
||||||
|
|||||||
@@ -115,13 +115,7 @@ fun TVTimeTheme(
|
|||||||
content = {
|
content = {
|
||||||
val systemUiController = rememberSystemUiController()
|
val systemUiController = rememberSystemUiController()
|
||||||
systemUiController.setStatusBarColor(color = androidx.compose.material3.MaterialTheme.colorScheme.background)
|
systemUiController.setStatusBarColor(color = androidx.compose.material3.MaterialTheme.colorScheme.background)
|
||||||
systemUiController.setNavigationBarColor(
|
systemUiController.setNavigationBarColor(color = androidx.compose.material3.MaterialTheme.colorScheme.background)
|
||||||
color = androidx.compose.material3.MaterialTheme.colorScheme.primary.copy(
|
|
||||||
alpha = 0.08f
|
|
||||||
).compositeOver(
|
|
||||||
background = androidx.compose.material3.MaterialTheme.colorScheme.surface
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class HomeTabRecyclerAdapter: RecyclerView.Adapter<HomeTabRecyclerAdapter.TabVie
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
val visiblePages = BottomNavItem.Items.filter { it.order > -1 }.sortedBy { it.order }
|
val visiblePages = BottomNavItem.Items.filter { it.order > -1 }.sortedBy { it.order }
|
||||||
val hiddenPages = BottomNavItem.Items.filter { it.order < 0 }
|
val hiddenPages = BottomNavItem.Items.filter { it.order == -1 }
|
||||||
pages = ArrayList<BottomNavItem?>().apply {
|
pages = ArrayList<BottomNavItem?>().apply {
|
||||||
addAll(visiblePages)
|
addAll(visiblePages)
|
||||||
add(null)
|
add(null)
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.owenlejeune.tvtime.utils
|
package com.owenlejeune.tvtime.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -18,7 +16,6 @@ import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AuthDeleteBody
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AuthRequestBody
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AuthRequestBody
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.V4AccountList
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.V4AccountList
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem
|
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -59,6 +56,12 @@ object SessionManager: KoinComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancelSignIn() {
|
||||||
|
if (currentSession.value is InProgressSession) {
|
||||||
|
currentSession.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun initialize() {
|
suspend fun initialize() {
|
||||||
preferences.authorizedSessionValues?.let { values ->
|
preferences.authorizedSessionValues?.let { values ->
|
||||||
val session = AuthorizedSession(
|
val session = AuthorizedSession(
|
||||||
@@ -79,17 +82,17 @@ object SessionManager: KoinComponent {
|
|||||||
val requestTokenResponse = service.createRequestToken(AuthRequestBody(redirect = "app://tvtime.auth.return"))
|
val requestTokenResponse = service.createRequestToken(AuthRequestBody(redirect = "app://tvtime.auth.return"))
|
||||||
if (requestTokenResponse.isSuccessful) {
|
if (requestTokenResponse.isSuccessful) {
|
||||||
requestTokenResponse.body()?.let { ctr ->
|
requestTokenResponse.body()?.let { ctr ->
|
||||||
currentSession.value = InProgressSession(ctr.requestToken)
|
|
||||||
val url = context.getString(R.string.tmdb_auth_url, ctr.requestToken)
|
val url = context.getString(R.string.tmdb_auth_url, ctr.requestToken)
|
||||||
val encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8.toString())
|
val encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8.toString())
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
onRedirect(encodedUrl)
|
onRedirect(encodedUrl)
|
||||||
}
|
}
|
||||||
|
currentSession.value = InProgressSession(ctr.requestToken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun singInPart2(
|
suspend fun signInPart2(
|
||||||
context: Context = get(Context::class.java)
|
context: Context = get(Context::class.java)
|
||||||
) {
|
) {
|
||||||
if (currentSession.value is InProgressSession) {
|
if (currentSession.value is InProgressSession) {
|
||||||
|
|||||||
@@ -114,6 +114,7 @@
|
|||||||
<string name="account_header_title_formatted">Hello, %1$s!</string>
|
<string name="account_header_title_formatted">Hello, %1$s!</string>
|
||||||
<string name="account_name_guest">Guest</string>
|
<string name="account_name_guest">Guest</string>
|
||||||
<string name="account_not_logged_in">Not logged in</string>
|
<string name="account_not_logged_in">Not logged in</string>
|
||||||
|
<string name="account_add">Add Account</string>
|
||||||
|
|
||||||
<string name="no_rated_content_message">No rated content</string>
|
<string name="no_rated_content_message">No rated content</string>
|
||||||
<string name="rating_test">Rating: %1$d</string>
|
<string name="rating_test">Rating: %1$d</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user