add items to about page and add attribution

This commit is contained in:
Owen LeJeune
2023-06-09 13:45:30 -04:00
parent 8e27b26cc6
commit a473171559
15 changed files with 342 additions and 242 deletions

View File

@@ -33,8 +33,8 @@ android {
} }
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_11
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
@@ -118,7 +118,7 @@ dependencies {
implementation "io.insert-koin:koin-android:$koin" implementation "io.insert-koin:koin-android:$koin"
// coil // coil
def coil = "2.0.0-rc01" def coil = "2.2.2"
implementation "io.coil-kt:coil-compose:$coil" implementation "io.coil-kt:coil-compose:$coil"
//Coroutines //Coroutines
@@ -135,9 +135,11 @@ dependencies {
def markdown = "0.2.1" def markdown = "0.2.1"
implementation "org.jetbrains:markdown:$markdown" implementation "org.jetbrains:markdown:$markdown"
implementation 'de.charlex.compose:revealswipe:1.0.0' def revealSwipe = "1.0.0"
implementation "de.charlex.compose:revealswipe:$revealSwipe"
implementation 'com.github.jeziellago:compose-markdown:0.3.3' def compose_markdown = "0.3.3"
implementation "com.github.jeziellago:compose-markdown:$compose_markdown"
// testing // testing
def junit = "4.13.2" def junit = "4.13.2"

View File

@@ -1,6 +1,8 @@
package com.owenlejeune.tvtime package com.owenlejeune.tvtime
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.window.OnBackInvokedDispatcher import android.window.OnBackInvokedDispatcher
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
@@ -40,6 +42,14 @@ import org.koin.android.ext.android.inject
@OptIn(ExperimentalPagerApi::class) @OptIn(ExperimentalPagerApi::class)
class OnboardingActivity: MonetCompatActivity() { class OnboardingActivity: MonetCompatActivity() {
companion object {
fun showActivity(sourceActivity: Context) {
val intent = Intent(sourceActivity, OnboardingActivity::class.java)
// intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
sourceActivity.startActivity(intent)
}
}
private val preferences: AppPreferences by inject() private val preferences: AppPreferences by inject()
private lateinit var pagerState: PagerState private lateinit var pagerState: PagerState
@@ -104,53 +114,56 @@ class OnboardingActivity: MonetCompatActivity() {
} }
} }
Box( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(all = 12.dp) .padding(all = 12.dp)
) { ) {
Button( OnboardingPage[pagerState.currentPage].footer.invoke(this@Column)
modifier = Modifier.align(Alignment.CenterStart), Row(
enabled = true, horizontalArrangement = Arrangement.SpaceBetween,
onClick = { modifier = Modifier.fillMaxWidth()
preferences.firstLaunch = false
launchActivity(MainActivity::class.java)
}
) { ) {
val skipText = if (preferences.firstLaunchTesting) Button(
stringResource(id = R.string.action_skip_testing) enabled = true,
else onClick = {
stringResource(id = R.string.action_skip)
Text(text = skipText)
}
HorizontalPagerIndicator(
pagerState = pagerState,
modifier = Modifier
.align(Alignment.Center)
.padding(16.dp),
activeColor = MaterialTheme.colorScheme.secondary
)
Button(
modifier = Modifier.align(Alignment.CenterEnd),
shape = CircleShape,
enabled = nextButtonEnabled.value,
onClick = {
if (isLastPage) {
preferences.firstLaunch = false preferences.firstLaunch = false
launchActivity(MainActivity::class.java) launchActivity(MainActivity::class.java)
} else { }
coroutineScope.launch { ) {
pagerState.animateScrollToPage(pagerState.currentPage + 1) val skipText = if (preferences.firstLaunchTesting)
stringResource(id = R.string.action_skip_testing)
else
stringResource(id = R.string.action_skip)
Text(text = skipText)
}
HorizontalPagerIndicator(
pagerState = pagerState,
modifier = Modifier
.padding(16.dp),
activeColor = MaterialTheme.colorScheme.secondary
)
Button(
shape = CircleShape,
enabled = nextButtonEnabled.value,
onClick = {
if (isLastPage) {
preferences.firstLaunch = false
launchActivity(MainActivity::class.java)
} else {
coroutineScope.launch {
pagerState.animateScrollToPage(pagerState.currentPage + 1)
}
} }
} }
} ) {
) { if (!isLastPage) {
if (!isLastPage) { Text(stringResource(id = R.string.get_started))
Text(stringResource(id = R.string.get_started)) } else {
} else { Icon(imageVector = Icons.Filled.ArrowForward, contentDescription = null)
Icon(imageVector = Icons.Filled.ArrowForward, contentDescription = null) }
} }
} }
} }
@@ -185,7 +198,7 @@ class OnboardingActivity: MonetCompatActivity() {
color = MaterialTheme.colorScheme.onBackground color = MaterialTheme.colorScheme.onBackground
) )
Spacer(modifier = Modifier.height(50.dp)) Spacer(modifier = Modifier.height(50.dp))
page.additionalContent(this@OnboardingActivity, nextButtonEnabled) page.additionalContent(this@Column, this@OnboardingActivity, nextButtonEnabled)
} }
} }

View File

@@ -45,27 +45,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@OptIn(ExperimentalPagerApi::class)
@Composable
fun Gallery(
pagerState: PagerState,
models: List<Any?>,
modifier: Modifier = Modifier,
contentDescriptions: List<String> = emptyList()
) {
HorizontalPager(
count = models.size,
state = pagerState,
modifier = modifier
) { page ->
AsyncImage(
model = models[page],
contentDescription = contentDescriptions[page],
contentScale = ContentScale.FillWidth
)
}
}
@OptIn(ExperimentalPagerApi::class) @OptIn(ExperimentalPagerApi::class)
@Composable @Composable
fun TapGallery( fun TapGallery(
@@ -145,7 +124,7 @@ fun TapGallery(
imageVector = Icons.Outlined.ChevronLeft, imageVector = Icons.Outlined.ChevronLeft,
contentDescription = null, contentDescription = null,
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
tint = MaterialTheme.colorScheme.surface tint = Color.White
) )
} }
@@ -160,114 +139,10 @@ fun TapGallery(
imageVector = Icons.Outlined.ChevronRight, imageVector = Icons.Outlined.ChevronRight,
contentDescription = null, contentDescription = null,
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
tint = MaterialTheme.colorScheme.surface tint = Color.White
) )
} }
// Box(
// modifier = Modifier
// .background(brush = leftGradient)
// .height(height = sizeImage.value.height.toDp())
// .width((sizeImage.value.width/2).toDp())
// .align(Alignment.CenterStart)
// .onGloballyPositioned { leftSizeImage.value = it.size }
// .clickable {
// val target =
// if (pagerState.currentPage == 0) models.size - 1 else pagerState.currentPage - 1
// scope.launch { pagerState.animateScrollToPage(target) }
// }
// ) {
// Icon(
// imageVector = Icons.Outlined.ChevronLeft,
// contentDescription = null,
// modifier = Modifier
// .size(48.dp)
// .padding(start = 24.dp)
// .align(Alignment.CenterStart),
// tint = MaterialTheme.colorScheme.surface
// )
// }
// Box(
// modifier = Modifier
// .background(brush = rightGradient)
// .height(height = sizeImage.value.height.toDp())
// .width((sizeImage.value.width/2).toDp())
// .align(Alignment.CenterEnd)
// .onGloballyPositioned { rightSizeImage.value = it.size }
// .clickable {
// val target =
// if (pagerState.currentPage == models.size - 1) 0 else pagerState.currentPage + 1
// scope.launch { pagerState.animateScrollToPage(target) }
// }
// ) {
// Icon(
// imageVector = Icons.Outlined.ChevronRight,
// contentDescription = null,
// modifier = Modifier
// .size(48.dp)
// .padding(end = 24.dp)
// .align(Alignment.CenterEnd),
// tint = MaterialTheme.colorScheme.surface
// )
// }
} }
} }
// AnimatedVisibility(
// visible = showControls.value,
// enter = fadeIn(),
// exit = fadeOut()
// ) {
// val leftSizeImage = remember { mutableStateOf(IntSize.Zero) }
// val leftGradient = Brush.horizontalGradient(
// colors = listOf(Color.Black, Color.Transparent),
// startX = 0f,
// endX = leftSizeImage.value.width.toFloat()
// )
// Box(
// modifier = Modifier
// .background(brush = leftGradient)
// .fillMaxHeight()
// .width(100.dp)
// .align(Alignment.CenterStart)
// .onGloballyPositioned { leftSizeImage.value = it.size }
// .clickable {
// val target =
// if (pagerState.currentPage == 0) models.size - 1 else pagerState.currentPage + 1
// scope.launch { pagerState.animateScrollToPage(target) }
// }
// ) {
// Icon(
// imageVector = Icons.Outlined.ChevronLeft,
// contentDescription = null,
// modifier = Modifier.size(48.dp)
// )
// }
//
// val rightSizeImage = remember { mutableStateOf(IntSize.Zero) }
// val rightGradient = Brush.horizontalGradient(
// colors = listOf(Color.Black, Color.Transparent),
// startX = leftSizeImage.value.width.toFloat(),
// endX = 0f
// )
// Box(
// modifier = Modifier
// .background(brush = rightGradient)
// .fillMaxHeight()
// .width(100.dp)
// .align(Alignment.CenterEnd)
// .onGloballyPositioned { rightSizeImage.value = it.size }
// .clickable {
// val target =
// if (pagerState.currentPage == models.size - 1) 0 else pagerState.currentPage - 1
// scope.launch { pagerState.animateScrollToPage(target) }
// }
// ) {
// Icon(
// imageVector = Icons.Outlined.ChevronRight,
// contentDescription = null,
// modifier = Modifier.size(48.dp)
// )
// }
// }
} }
} }

View File

@@ -12,7 +12,9 @@ import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.Login import androidx.compose.material.icons.outlined.Login
@@ -30,9 +32,11 @@ import androidx.compose.runtime.Composable
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.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@@ -213,12 +217,27 @@ fun ProfileMenuOverlay(
MenuDivider() MenuDivider()
Text( Row(
text = "${stringResource(id = R.string.app_name)} v${BuildConfig.VERSION_NAME}", modifier = Modifier.align(Alignment.CenterHorizontally),
fontSize = 10.sp, horizontalArrangement = Arrangement.spacedBy(4.dp),
textAlign = TextAlign.Center, verticalAlignment = Alignment.CenterVertically
modifier = Modifier.fillMaxWidth() ) {
) Text(
text = "${stringResource(id = R.string.app_name)} v${BuildConfig.VERSION_NAME}",
fontSize = 10.sp
)
Text(text = "")
Icon(
painter = painterResource(id = R.drawable.tmdb_logo),
tint = Color.Unspecified,
contentDescription = null,
modifier = Modifier.size(16.dp)
)
Text(
text = stringResource(R.string.powered_by_tmdb),
fontSize = 10.sp
)
}
} }
} }
} }
@@ -237,6 +256,8 @@ private fun ProfileMenuItem(
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(10.dp))
.padding(horizontal = 4.dp)
.clickable( .clickable(
enabled = onClick != null, enabled = onClick != null,
onClick = onClick ?: {} onClick = onClick ?: {}

View File

@@ -25,6 +25,7 @@ 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 coil.compose.AsyncImage import coil.compose.AsyncImage
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.api.tmdb.api.v3.DetailService import com.owenlejeune.tvtime.api.tmdb.api.v3.DetailService
import com.owenlejeune.tvtime.api.tmdb.api.v3.MoviesService import com.owenlejeune.tvtime.api.tmdb.api.v3.MoviesService
@@ -47,6 +48,10 @@ fun SearchScreen(
title: String, title: String,
mediaViewType: MediaViewType mediaViewType: MediaViewType
) { ) {
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(color = MaterialTheme.colorScheme.background)
systemUiController.setNavigationBarColor(color = MaterialTheme.colorScheme.background)
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()

View File

@@ -8,10 +8,16 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.outlined.Description import androidx.compose.material.icons.outlined.Description
@@ -34,13 +40,17 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
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.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
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.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
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.NavController import androidx.navigation.NavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.BuildConfig import com.owenlejeune.tvtime.BuildConfig
import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.utils.FileUtils import com.owenlejeune.tvtime.utils.FileUtils
@@ -51,6 +61,10 @@ import dev.jeziellago.compose.markdowntext.MarkdownText
fun AboutView( fun AboutView(
appNavController: NavController appNavController: NavController
) { ) {
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(color = MaterialTheme.colorScheme.background)
systemUiController.setNavigationBarColor(color = MaterialTheme.colorScheme.background)
val context = LocalContext.current val context = LocalContext.current
val decayAnimationSpec = rememberSplineBasedDecay<Float>() val decayAnimationSpec = rememberSplineBasedDecay<Float>()
@@ -78,11 +92,10 @@ fun AboutView(
) { ) {
Box(modifier = Modifier.padding(it)) { Box(modifier = Modifier.padding(it)) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(24.dp), modifier = Modifier.padding(horizontal = 12.dp)
modifier = Modifier.padding(horizontal = 24.dp)
) { ) {
AboutItem( AboutItem(
title = "App Info", title = stringResource(R.string.app_info_label),
subtitle = "v${BuildConfig.VERSION_NAME}", subtitle = "v${BuildConfig.VERSION_NAME}",
icon = Icons.Outlined.Info, icon = Icons.Outlined.Info,
onClick = { onClick = {
@@ -96,7 +109,7 @@ fun AboutView(
var showChangeLog by remember { mutableStateOf(false) } var showChangeLog by remember { mutableStateOf(false) }
AboutItem( AboutItem(
title = "Changelog", title = stringResource(R.string.changelog_label),
icon = Icons.Outlined.Description, icon = Icons.Outlined.Description,
onClick = { showChangeLog = true } onClick = { showChangeLog = true }
) )
@@ -104,6 +117,18 @@ fun AboutView(
visible = showChangeLog, visible = showChangeLog,
onDismissRequest = { showChangeLog = false } onDismissRequest = { showChangeLog = false }
) )
AboutItem(
title = "Privacy Policy",
onClick = {}
)
AboutItem(
title = "Terms of Use",
onClick = {}
)
AttributionSection()
} }
} }
} }
@@ -113,29 +138,34 @@ fun AboutView(
private fun AboutItem( private fun AboutItem(
title: String, title: String,
subtitle: String? = null, subtitle: String? = null,
icon: ImageVector, icon: ImageVector? = null,
onClick: (() -> Unit)? = null onClick: (() -> Unit)? = null
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.wrapContentHeight() .wrapContentHeight()
.clip(RoundedCornerShape(10.dp))
.clickable( .clickable(
onClick = onClick ?: {}, onClick = onClick ?: {},
enabled = onClick != null enabled = onClick != null
), )
horizontalArrangement = Arrangement.spacedBy(24.dp)
) { ) {
Icon( icon?.let {
imageVector = icon, Icon(
contentDescription = subtitle, imageVector = icon,
modifier = Modifier.align(Alignment.CenterVertically), contentDescription = subtitle,
tint = MaterialTheme.colorScheme.primary modifier = Modifier
) .align(Alignment.CenterVertically)
.padding(all = 12.dp),
tint = MaterialTheme.colorScheme.primary
)
}
Column( Column(
modifier = Modifier modifier = Modifier
.align(Alignment.CenterVertically) .align(Alignment.CenterVertically)
.weight(1f) .weight(1f)
.padding(all = 12.dp)
) { ) {
val titleColor = MaterialTheme.colorScheme.onBackground val titleColor = MaterialTheme.colorScheme.onBackground
val subtitleColor = MaterialTheme.colorScheme.onSurfaceVariant val subtitleColor = MaterialTheme.colorScheme.onSurfaceVariant
@@ -161,12 +191,54 @@ private fun ChangeLogDialog(
Button( Button(
onClick = onDismissRequest onClick = onDismissRequest
) { ) {
Text(text = "Close") Text(text = stringResource(id = R.string.action_dismiss))
} }
}, },
text = { text = {
MarkdownText(markdown = changeLog) MarkdownText(
markdown = changeLog,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
} }
) )
} }
}
@Composable
private fun ColumnScope.AttributionSection() {
val context = LocalContext.current
Spacer(modifier = Modifier.weight(1f))
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.clip(RoundedCornerShape(10.dp))
.clickable(
onClick = {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.tmdb_home_page)))
context.startActivity(intent)
}
)
) {
Spacer(modifier = Modifier.height(12.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Spacer(modifier = Modifier.width(12.dp))
Icon(
painter = painterResource(id = R.drawable.tmdb_logo),
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.Unspecified
)
Text(
text = stringResource(id = R.string.attribution_text),
fontSize = 12.sp
)
Spacer(modifier = Modifier.width(12.dp))
}
Spacer(modifier = Modifier.height(12.dp))
}
} }

View File

@@ -8,9 +8,11 @@ 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.getValue
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.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -24,6 +26,7 @@ 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
import com.google.accompanist.pager.rememberPagerState import com.google.accompanist.pager.rememberPagerState
import com.google.accompanist.systemuicontroller.rememberSystemUiController
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
@@ -48,59 +51,67 @@ fun AccountView(
appNavController: NavHostController, appNavController: NavHostController,
doSignInPartTwo: Boolean = false doSignInPartTwo: Boolean = false
) { ) {
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(color = MaterialTheme.colorScheme.background)
systemUiController.setNavigationBarColor(color = MaterialTheme.colorScheme.background)
val currentSessionState = remember { SessionManager.currentSession } val currentSessionState = remember { SessionManager.currentSession }
val currentSession = currentSessionState.value val currentSession = currentSessionState.value
val showProfileMenuOverlay = remember { mutableStateOf(false) } val decayAnimationSpec = rememberSplineBasedDecay<Float>()
val topAppBarScrollState = rememberTopAppBarScrollState()
ProfileMenuContainer( val scrollBehavior = remember(decayAnimationSpec) {
appNavController = appNavController, TopAppBarDefaults.exitUntilCollapsedScrollBehavior(decayAnimationSpec, topAppBarScrollState)
visible = showProfileMenuOverlay.value, }
onDismissRequest = { showProfileMenuOverlay.value = false } Scaffold(
) { modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
val decayAnimationSpec = rememberSplineBasedDecay<Float>() topBar = {
val topAppBarScrollState = rememberTopAppBarScrollState() LargeTopAppBar(
val scrollBehavior = remember(decayAnimationSpec) { scrollBehavior = scrollBehavior,
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(decayAnimationSpec, topAppBarScrollState) navigationIcon = {
} IconButton(
Scaffold( onClick = { appNavController.popBackStack() }
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), ) {
topBar = { Icon(
LargeTopAppBar( Icons.Filled.ArrowBack,
scrollBehavior = scrollBehavior, contentDescription = null
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))
} }
) },
} title = {
) { if (currentSession?.isAuthorized == false) {
Box(modifier = Modifier.padding(it)) { Text(text = stringResource(id = R.string.account_not_logged_in))
AccountViewContent(appNavController = appNavController, doSignInPartTwo = doSignInPartTwo) } else {
} val accountDetails = remember { currentSession!!.accountDetails }
Text(text = getAccountName(accountDetails.value))
}
},
colors = TopAppBarDefaults.largeTopAppBarColors(scrolledContainerColor = MaterialTheme.colorScheme.background),
actions = {
var showDropDownMenu by remember { mutableStateOf(false) }
AccountIcon(
modifier = Modifier.padding(end = 8.dp),
size = 32.dp,
onClick = { showDropDownMenu = true }
)
DropdownMenu(
expanded = showDropDownMenu,
onDismissRequest = { showDropDownMenu = false}
) {
DropdownMenuItem(
text = { Text(text = stringResource(id = R.string.action_sign_out)) },
onClick = {
SessionManager.clearSession()
appNavController.popBackStack()
}
)
}
}
)
}
) {
Box(modifier = Modifier.padding(it)) {
AccountViewContent(appNavController = appNavController, doSignInPartTwo = doSignInPartTwo)
} }
} }
} }

View File

@@ -33,6 +33,7 @@ import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension import androidx.constraintlayout.compose.Dimension
import androidx.navigation.NavController import androidx.navigation.NavController
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.R 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.model.MarkAsFavoriteBody import com.owenlejeune.tvtime.api.tmdb.api.v3.model.MarkAsFavoriteBody
@@ -65,6 +66,10 @@ fun ListDetailView(
windowSize: WindowSizeClass, windowSize: WindowSizeClass,
preferences: AppPreferences = KoinJavaComponent.get(AppPreferences::class.java) preferences: AppPreferences = KoinJavaComponent.get(AppPreferences::class.java)
) { ) {
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(color = MaterialTheme.colorScheme.background)
systemUiController.setNavigationBarColor(color = MaterialTheme.colorScheme.background)
val service = ListV4Service() val service = ListV4Service()
val parentList = remember { mutableStateOf<MediaList?>(null) } val parentList = remember { mutableStateOf<MediaList?>(null) }

View File

@@ -37,6 +37,7 @@ import com.google.accompanist.flowlayout.FlowRow
import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.PagerState import com.google.accompanist.pager.PagerState
import com.google.accompanist.pager.rememberPagerState import com.google.accompanist.pager.rememberPagerState
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.R 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.DetailService import com.owenlejeune.tvtime.api.tmdb.api.v3.DetailService
@@ -69,6 +70,10 @@ fun MediaDetailView(
windowSize: WindowSizeClass, windowSize: WindowSizeClass,
preferences: AppPreferences = get(AppPreferences::class.java) preferences: AppPreferences = get(AppPreferences::class.java)
) { ) {
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(color = MaterialTheme.colorScheme.background)
systemUiController.setNavigationBarColor(color = MaterialTheme.colorScheme.background)
val service = when (type) { val service = when (type) {
MediaViewType.MOVIE -> MoviesService() MediaViewType.MOVIE -> MoviesService()
MediaViewType.TV -> TvService() MediaViewType.TV -> TvService()

View File

@@ -20,6 +20,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavController import androidx.navigation.NavController
import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.api.tmdb.api.v3.PeopleService import com.owenlejeune.tvtime.api.tmdb.api.v3.PeopleService
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailPerson import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailPerson
@@ -40,6 +41,10 @@ fun PersonDetailView(
appNavController: NavController, appNavController: NavController,
personId: Int? personId: Int?
) { ) {
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(color = MaterialTheme.colorScheme.background)
systemUiController.setNavigationBarColor(color = MaterialTheme.colorScheme.background)
val person = remember { mutableStateOf<DetailPerson?>(null) } val person = remember { mutableStateOf<DetailPerson?>(null) }
personId?.let { personId?.let {
if (person.value == null) { if (person.value == null) {

View File

@@ -30,8 +30,10 @@ import androidx.navigation.NavController
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.kieronquinn.monetcompat.core.MonetCompat import com.kieronquinn.monetcompat.core.MonetCompat
import com.owenlejeune.tvtime.BuildConfig import com.owenlejeune.tvtime.BuildConfig
import com.owenlejeune.tvtime.OnboardingActivity
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.ui.components.* import com.owenlejeune.tvtime.ui.components.*
@@ -53,6 +55,10 @@ fun SettingsTab(
route: String? = null, route: String? = null,
preferences: AppPreferences = get(AppPreferences::class.java) preferences: AppPreferences = get(AppPreferences::class.java)
) { ) {
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(color = MaterialTheme.colorScheme.background)
systemUiController.setNavigationBarColor(color = MaterialTheme.colorScheme.background)
val decayAnimationSpec = rememberSplineBasedDecay<Float>() val decayAnimationSpec = rememberSplineBasedDecay<Float>()
val topAppBarScrollState = rememberTopAppBarScrollState() val topAppBarScrollState = rememberTopAppBarScrollState()
val scrollBehavior = remember(decayAnimationSpec) { val scrollBehavior = remember(decayAnimationSpec) {
@@ -445,6 +451,15 @@ private fun DevPreferences(
preferences.firstLaunchTesting = isChecked preferences.firstLaunchTesting = isChecked
} }
) )
Text(
text = "Show onboarding UI",
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 12.dp)
.clickable {
OnboardingActivity.showActivity(context)
}
)
val shouldShowPalette = remember { mutableStateOf(false) } val shouldShowPalette = remember { mutableStateOf(false) }
Text( Text(

View File

@@ -14,11 +14,13 @@ import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.SmallTopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.navigation.NavController import androidx.navigation.NavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
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
@@ -32,6 +34,10 @@ fun WebLinkView(
url: String, url: String,
appNavController: NavController appNavController: NavController
) { ) {
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(color = MaterialTheme.colorScheme.background)
systemUiController.setNavigationBarColor(color = MaterialTheme.colorScheme.background)
Scaffold( Scaffold(
topBar = { topBar = {
SmallTopAppBar( SmallTopAppBar(

View File

@@ -4,8 +4,16 @@ import android.os.Build
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Brightness6 import androidx.compose.material.icons.filled.Brightness6
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
@@ -13,13 +21,19 @@ import androidx.compose.material.icons.outlined.DarkMode
import androidx.compose.material.icons.outlined.LightMode import androidx.compose.material.icons.outlined.LightMode
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
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 com.kieronquinn.monetcompat.core.MonetCompat import com.kieronquinn.monetcompat.core.MonetCompat
import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.preferences.AppPreferences import com.owenlejeune.tvtime.preferences.AppPreferences
@@ -34,7 +48,8 @@ sealed class OnboardingPage(
@StringRes val title: Int, @StringRes val title: Int,
@StringRes val description: Int, @StringRes val description: Int,
@DrawableRes val image: Int? = null, @DrawableRes val image: Int? = null,
val additionalContent: @Composable (AppCompatActivity, MutableState<Boolean>) -> Unit = { a, e -> } val additionalContent: @Composable ColumnScope.(AppCompatActivity, MutableState<Boolean>) -> Unit = { _, _ -> },
val footer: @Composable ColumnScope.() -> Unit = {}
) { ) {
companion object: KoinComponent { companion object: KoinComponent {
@@ -49,7 +64,29 @@ sealed class OnboardingPage(
order = 0, order = 0,
title = R.string.app_name, title = R.string.app_name,
description = R.string.intro_page_desc, description = R.string.intro_page_desc,
image = R.drawable.ic_launcher_foreground image = R.drawable.ic_launcher_foreground,
footer = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier
.align(Alignment.CenterHorizontally)
.wrapContentWidth()
.padding(all = 24.dp)
) {
Icon(
painter = painterResource(id = R.drawable.tmdb_logo),
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.Unspecified
)
Text(
text = stringResource(id = R.string.attribution_text),
fontSize = 12.sp,
textAlign = TextAlign.Center
)
}
}
) )
object SignInPage: OnboardingPage( object SignInPage: OnboardingPage(

View File

@@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="185.04dp"
android:height="133.4dp"
android:viewportWidth="185.04"
android:viewportHeight="133.4">
<path
android:pathData="M51.06,66.7h0A17.67,17.67 0,0 1,68.73 49h-0.1A17.67,17.67 0,0 1,86.3 66.7h0A17.67,17.67 0,0 1,68.63 84.37h0.1A17.67,17.67 0,0 1,51.06 66.7ZM133.73,35.37h32.9A17.67,17.67 0,0 0,184.3 17.7h0A17.67,17.67 0,0 0,166.63 0h-32.9A17.67,17.67 0,0 0,116.06 17.7h0A17.67,17.67 0,0 0,133.73 35.37ZM20.73,133.37h63.9A17.67,17.67 0,0 0,102.3 115.7h0A17.67,17.67 0,0 0,84.63 98L20.73,98A17.67,17.67 0,0 0,3.06 115.7h0A17.67,17.67 0,0 0,20.73 133.37ZM104.65,84.37h6.25L125.5,49h-8.35l-8.9,23.2h-0.1L99.4,49L90.5,49ZM137.1,84.37h7.8L144.9,49h-7.8ZM159.3,84.37h24.95L184.25,77.2L167.1,77.2L167.1,70h15.35L182.45,62.8L167.1,62.8L167.1,56.2h16.25L183.35,49h-24ZM10.1,35.4h7.8L17.9,6.9L28,6.9L28,0L0,0L0,6.9L10.1,6.9ZM39,35.4h7.8L46.8,20.1L61.9,20.1L61.9,35.4h7.8L69.7,0L61.9,0L61.9,13.2L46.75,13.2L46.75,0L39,0ZM80.25,35.4h25L105.25,28.2L88,28.2L88,21h15.35L103.35,13.8L88,13.8L88,7.2h16.25L104.25,0h-24ZM1.25,84.4L9,84.4L9,57.25h0.1l9,27.15L24,84.4l9.3,-27.15h0.1L33.4,84.4h7.8L41.2,49L29.45,49l-8.2,23.1h-0.1L13,49L1.2,49ZM113.34,133.4L126,133.4a24.59,24.59 0,0 0,7.56 -1.15,19.52 19.52,0 0,0 6.35,-3.37 16.37,16.37 0,0 0,4.37 -5.5A16.91,16.91 0,0 0,146 115.8a18.5,18.5 0,0 0,-1.68 -8.25,15.1 15.1,0 0,0 -4.52,-5.53A18.55,18.55 0,0 0,133.07 99,33.54 33.54,0 0,0 125,98L113.29,98ZM121.15,105.2h4.6a17.43,17.43 0,0 1,4.67 0.62,11.68 11.68,0 0,1 3.88,1.88 9,9 0,0 1,2.62 3.18,9.87 9.87,0 0,1 1,4.52 11.92,11.92 0,0 1,-1 5.08,8.69 8.69,0 0,1 -2.67,3.34 10.87,10.87 0,0 1,-4 1.83,21.57 21.57,0 0,1 -5,0.55L121.1,126.2ZM157.29,133.4h14.5a23.11,23.11 0,0 0,4.73 -0.5,13.38 13.38,0 0,0 4.27,-1.65 9.42,9.42 0,0 0,3.1 -3,8.52 8.52,0 0,0 1.2,-4.68 9.16,9.16 0,0 0,-0.55 -3.2,7.79 7.79,0 0,0 -1.57,-2.62 8.38,8.38 0,0 0,-2.45 -1.85,10 10,0 0,0 -3.18,-1v-0.1a9.28,9.28 0,0 0,4.43 -2.82,7.42 7.42,0 0,0 1.67,-5 8.34,8.34 0,0 0,-1.15 -4.65,7.88 7.88,0 0,0 -3,-2.73 12.9,12.9 0,0 0,-4.17 -1.3,34.42 34.42,0 0,0 -4.63,-0.32h-13.2ZM165.09,104.6h5.3a10.79,10.79 0,0 1,1.85 0.17,5.77 5.77,0 0,1 1.7,0.58 3.33,3.33 0,0 1,1.23 1.13,3.22 3.22,0 0,1 0.47,1.82 3.63,3.63 0,0 1,-0.42 1.8,3.34 3.34,0 0,1 -1.13,1.2 4.78,4.78 0,0 1,-1.57 0.65,8.16 8.16,0 0,1 -1.78,0.2L165,112.15ZM165.09,118.75h5.9a15.12,15.12 0,0 1,2.05 0.15,7.83 7.83,0 0,1 2,0.55 4,4 0,0 1,1.58 1.17,3.13 3.13,0 0,1 0.62,2 3.71,3.71 0,0 1,-0.47 1.95,4 4,0 0,1 -1.23,1.3 4.78,4.78 0,0 1,-1.67 0.7,8.91 8.91,0 0,1 -1.83,0.2h-7Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="0"
android:startY="66.7"
android:endX="185.04"
android:endY="66.7"
android:type="linear">
<item android:offset="0" android:color="#FF90CEA1"/>
<item android:offset="0.56" android:color="#FF3CBEC9"/>
<item android:offset="1" android:color="#FF00B3E5"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@@ -215,4 +215,10 @@
<string name="recommended_tv_title">Recommended TV</string> <string name="recommended_tv_title">Recommended TV</string>
<string name="no_recommended_tv">No Recommended TV</string> <string name="no_recommended_tv">No Recommended TV</string>
<string name="no_result_found">No more results found</string> <string name="no_result_found">No more results found</string>
<string name="attribution_text">This product uses the TMDB API but is not endorsed or certified by TMDB.</string>
<string name="tmdb_home_page">"https://www.themoviedb.org"</string>
<string name="app_info_label">App Info</string>
<string name="changelog_label">Changelog</string>
<string name="powered_by_tmdb">Powered by TMDB</string>
</resources> </resources>