add basis for onboarding flow and some more theming options

This commit is contained in:
Owen LeJeune
2022-06-22 14:51:51 -04:00
parent d997b7bf46
commit 58b740cfb0
42 changed files with 692 additions and 204 deletions

View File

@@ -13,15 +13,25 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.TVTime" android:theme="@style/Theme.TVTime"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
<activity android:name=".AppRoutingActivity"
android:exported="true"
android:theme="@style/Theme.TVTime">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".OnboardingActivity"
android:exported="true"
android:theme="@style/Theme.TVTime" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:theme="@style/Theme.TVTime" android:theme="@style/Theme.TVTime"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />

View File

@@ -0,0 +1,23 @@
package com.owenlejeune.tvtime
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.owenlejeune.tvtime.extensions.launchActivity
import com.owenlejeune.tvtime.preferences.AppPreferences
import org.koin.android.ext.android.inject
class AppRoutingActivity: AppCompatActivity() {
private val preferences: AppPreferences by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (preferences.firstLaunch) {
launchActivity(OnboardingActivity::class.java)
} else {
launchActivity(MainActivity::class.java)
}
}
}

View File

@@ -1,7 +1,6 @@
package com.owenlejeune.tvtime package com.owenlejeune.tvtime
import android.os.Bundle import android.os.Bundle
import android.widget.Toast
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.animation.rememberSplineBasedDecay import androidx.compose.animation.rememberSplineBasedDecay
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
@@ -37,9 +36,9 @@ import com.owenlejeune.tvtime.ui.components.SearchFab
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
import com.owenlejeune.tvtime.ui.screens.MediaDetailView import com.owenlejeune.tvtime.ui.screens.main.MediaDetailView
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.ui.screens.PersonDetailView import com.owenlejeune.tvtime.ui.screens.main.PersonDetailView
import com.owenlejeune.tvtime.ui.theme.TVTimeTheme import com.owenlejeune.tvtime.ui.theme.TVTimeTheme
import com.owenlejeune.tvtime.utils.KeyboardManager import com.owenlejeune.tvtime.utils.KeyboardManager
import com.owenlejeune.tvtime.utils.SessionManager import com.owenlejeune.tvtime.utils.SessionManager

View File

@@ -0,0 +1,156 @@
package com.owenlejeune.tvtime
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.lifecycleScope
import com.google.accompanist.pager.*
import com.kieronquinn.monetcompat.app.MonetCompatActivity
import com.owenlejeune.tvtime.extensions.launchActivity
import com.owenlejeune.tvtime.preferences.AppPreferences
import com.owenlejeune.tvtime.ui.screens.onboarding.OnboardingPage
import com.owenlejeune.tvtime.ui.theme.TVTimeTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
@OptIn(ExperimentalPagerApi::class)
class OnboardingActivity: MonetCompatActivity() {
private val preferences: AppPreferences by inject()
private lateinit var pagerState: PagerState
private lateinit var coroutineScope: CoroutineScope
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launchWhenCreated {
monet.awaitMonetReady()
setContent {
TVTimeTheme(monetCompat = monet) {
OnboardingUi()
}
}
}
}
@Composable
fun OnboardingUi() {
pagerState = rememberPagerState()
coroutineScope = rememberCoroutineScope()
Column(
modifier = Modifier.background(color = MaterialTheme.colorScheme.background)
) {
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.weight(1f),
// .background(MaterialTheme.colorScheme.primary),
count = OnboardingPage.values().size,
userScrollEnabled = false
) { page ->
OnboardingPageUi(page = OnboardingPage[page])
}
Box(
modifier = Modifier
.fillMaxWidth()
.padding(all = 12.dp)
) {
HorizontalPagerIndicator(
pagerState = pagerState,
modifier = Modifier
.align(Alignment.Center)
.padding(16.dp),
activeColor = MaterialTheme.colorScheme.secondary
)
val isLastPage = pagerState.currentPage == OnboardingPage.values().size - 1
Button(
modifier = Modifier.align(Alignment.CenterEnd),
shape = CircleShape,
onClick = {
if (isLastPage) {
preferences.firstLaunch = false
launchActivity(MainActivity::class.java)
} else {
coroutineScope.launch {
pagerState.animateScrollToPage(pagerState.currentPage + 1)
}
}
}
) {
if (isLastPage) {
Text(stringResource(id = R.string.get_started))
} else {
Icon(imageVector = Icons.Filled.ArrowForward, contentDescription = null)
}
}
}
}
}
@Composable
fun OnboardingPageUi(page: OnboardingPage) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
page.image?.let {
Image(
painter = painterResource(id = it),
contentDescription = null,
modifier = Modifier.size(200.dp),
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
)
Spacer(modifier = Modifier.height(20.dp))
}
Text(
text = stringResource(id = page.title),
fontSize = 28.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onBackground
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(id = page.description),
textAlign = TextAlign.Center,
fontSize = 14.sp,
color = MaterialTheme.colorScheme.onBackground
)
Spacer(modifier = Modifier.height(12.dp))
page.additionalContent(this@OnboardingActivity)
}
}
override fun onBackPressed() {
if (pagerState.currentPage == 0) {
finish()
} else {
coroutineScope.launch {
pagerState.animateScrollToPage(pagerState.currentPage - 1)
}
}
}
}

View File

@@ -6,6 +6,9 @@ import com.kieronquinn.monetcompat.core.MonetCompat
import com.owenlejeune.tvtime.di.modules.appModule import com.owenlejeune.tvtime.di.modules.appModule
import com.owenlejeune.tvtime.di.modules.networkModule import com.owenlejeune.tvtime.di.modules.networkModule
import com.owenlejeune.tvtime.di.modules.preferencesModule import com.owenlejeune.tvtime.di.modules.preferencesModule
import com.owenlejeune.tvtime.preferences.AppPreferences
import dev.kdrag0n.monet.factory.ColorSchemeFactory
import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
@@ -13,6 +16,8 @@ import org.koin.core.logger.Level
class TvTimeApplication: Application() { class TvTimeApplication: Application() {
private val preferences: AppPreferences by inject()
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@@ -30,6 +35,12 @@ class TvTimeApplication: Application() {
} }
MonetCompat.enablePaletteCompat() MonetCompat.enablePaletteCompat()
MonetCompat.chromaMultiplier = preferences.chromaMultiplier
MonetCompat.wallpaperColorPicker = {
val userPickedColor = preferences.selectedColor
it?.firstOrNull { color -> color == userPickedColor } ?: it?.firstOrNull()
}
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Stetho.initializeWithDefaults(this) Stetho.initializeWithDefaults(this)

View File

@@ -2,7 +2,7 @@ package com.owenlejeune.tvtime.api.tmdb.api.v3.deserializer
import com.google.gson.* import com.google.gson.*
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KnownFor import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KnownFor
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import java.lang.reflect.Type import java.lang.reflect.Type
class KnownForDeserializer: JsonDeserializer<KnownFor> { class KnownForDeserializer: JsonDeserializer<KnownFor> {

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class DetailCast( class DetailCast(
@SerializedName("id") val id: Int, @SerializedName("id") val id: Int,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class DetailCrew( class DetailCrew(
@SerializedName("id") val id: Int, @SerializedName("id") val id: Int,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class KnowForTv( class KnowForTv(
backdropPath: String?, backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
abstract class KnownFor( abstract class KnownFor(
@SerializedName("backdrop_path") val backdropPath: String?, @SerializedName("backdrop_path") val backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class KnownForMovie( class KnownForMovie(
backdropPath: String?, backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class MarkAsFavoriteBody( class MarkAsFavoriteBody(
@SerializedName("media_type") val mediaType: MediaViewType, @SerializedName("media_type") val mediaType: MediaViewType,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class WatchlistBody( class WatchlistBody(
@SerializedName("media_type") val mediaType: MediaViewType, @SerializedName("media_type") val mediaType: MediaViewType,

View File

@@ -4,7 +4,7 @@ import com.google.gson.*
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListItem import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListItem
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListMovie import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListMovie
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListTv import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListTv
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import java.lang.reflect.Type import java.lang.reflect.Type
class ListItemDeserializer: JsonDeserializer<ListItem> { class ListItemDeserializer: JsonDeserializer<ListItem> {

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class AddToListBody( class AddToListBody(
@SerializedName("items") val items: List<AddToListBodyItem> @SerializedName("items") val items: List<AddToListBodyItem>

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class DeleteListItemsBody( class DeleteListItemsBody(
@SerializedName("media_id") val id: Int, @SerializedName("media_id") val id: Int,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
abstract class ListItem( abstract class ListItem(
@SerializedName("backdrop_path") val backdropPath: String?, @SerializedName("backdrop_path") val backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class ListItemStatusResponse( class ListItemStatusResponse(
statusMessage: String, statusMessage: String,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class ListMovie( class ListMovie(
backdropPath: String?, backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class ListTv( class ListTv(
backdropPath: String?, backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class UpdateListItemBody( class UpdateListItemBody(
@SerializedName("items") val items: List<UpdateListItemBodyItem> @SerializedName("items") val items: List<UpdateListItemBodyItem>

View File

@@ -1,6 +1,7 @@
package com.owenlejeune.tvtime.extensions package com.owenlejeune.tvtime.extensions
import android.app.Activity import android.app.Activity
import android.content.Intent
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.Size
@@ -39,4 +40,10 @@ private fun getWindowSizeClass(windowDpSize: DpSize): WindowSizeClass = when {
windowDpSize.width < 600.dp -> WindowSizeClass.Compact windowDpSize.width < 600.dp -> WindowSizeClass.Compact
windowDpSize.width < 840.dp -> WindowSizeClass.Medium windowDpSize.width < 840.dp -> WindowSizeClass.Medium
else -> WindowSizeClass.Expanded else -> WindowSizeClass.Expanded
}
fun <T> Activity.launchActivity(activity: Class<T>) {
val intent = Intent(this, activity)
startActivity(intent)
finish()
} }

View File

@@ -2,6 +2,8 @@ package com.owenlejeune.tvtime.preferences
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import com.kieronquinn.monetcompat.core.MonetCompat
import com.owenlejeune.tvtime.BuildConfig
class AppPreferences(context: Context) { class AppPreferences(context: Context) {
@@ -13,6 +15,11 @@ class AppPreferences(context: Context) {
private val HIDE_TITLE = "hide_title" private val HIDE_TITLE = "hide_title"
private val GUEST_SESSION = "guest_session_id" private val GUEST_SESSION = "guest_session_id"
private val AUTHORIZED_SESSION = "authorized_session_id" private val AUTHORIZED_SESSION = "authorized_session_id"
private val FIRST_LAUNCH = "first_launch"
private val FIRST_LAUNCH_TESTING = "first_launch_testing"
private val CHROMA_MULTIPLIER = "chroma_multiplier"
private val USE_SYSTEM_COLORS = "use_system_colors"
private val SELECTED_COLOR = "selected_color"
} }
private val preferences: SharedPreferences = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE) private val preferences: SharedPreferences = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE)
@@ -37,6 +44,26 @@ class AppPreferences(context: Context) {
// get() = preferences.getBoolean(USE_PREFERENCES, false) // get() = preferences.getBoolean(USE_PREFERENCES, false)
// set(value) { preferences.put(USE_PREFERENCES, value) } // set(value) { preferences.put(USE_PREFERENCES, value) }
var firstLaunchTesting: Boolean
get() = preferences.getBoolean(FIRST_LAUNCH_TESTING, false)
set(value) { preferences.put(FIRST_LAUNCH_TESTING, value) }
var firstLaunch: Boolean
get() = if (BuildConfig.DEBUG) firstLaunchTesting else preferences.getBoolean(FIRST_LAUNCH, true)
set(value) { preferences.put(FIRST_LAUNCH, value) }
var useSystemColors: Boolean
get() = preferences.getBoolean(USE_SYSTEM_COLORS, true)
set(value) { preferences.put(USE_SYSTEM_COLORS, value) }
var chromaMultiplier: Double
get() = preferences.getFloat(CHROMA_MULTIPLIER, MonetCompat.chromaMultiplier.toFloat()).toDouble()
set(value) { preferences.put(CHROMA_MULTIPLIER, value) }
var selectedColor: Int
get() = preferences.getInt(SELECTED_COLOR, Int.MAX_VALUE)
set(value) { preferences.put(SELECTED_COLOR, value) }
private fun SharedPreferences.put(key: String, value: Any?) { private fun SharedPreferences.put(key: String, value: Any?) {
edit().apply { edit().apply {
when (value) { when (value) {
@@ -44,12 +71,16 @@ class AppPreferences(context: Context) {
is Int -> putInt(key, value) is Int -> putInt(key, value)
is Long -> putLong(key, value) is Long -> putLong(key, value)
is Float -> putFloat(key, value) is Float -> putFloat(key, value)
is Double -> putFloat(key, value.toFloat())
is String -> putString(key, value) is String -> putString(key, value)
else -> throw UnsupportedTypeError()
} }
apply() apply()
} }
} }
class UnsupportedTypeError: Exception()
// private fun <T> SharedPreferences.get(key: String, java: Class<T>): T { // private fun <T> SharedPreferences.get(key: String, java: Class<T>): T {
// //
// } // }

View File

@@ -1,9 +1,11 @@
package com.owenlejeune.tvtime.ui.components package com.owenlejeune.tvtime.ui.components
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.ui.Alignment 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.graphics.Color
@@ -73,4 +75,41 @@ fun SwitchPreference(
@Composable @Composable
private fun SwitchPreferencePreview() { private fun SwitchPreferencePreview() {
SwitchPreference("Title", true, subtitleText = "Subtitle") SwitchPreference("Title", true, subtitleText = "Subtitle")
}
@Composable
fun SliderPreference(
modifier: Modifier = Modifier,
titleText: String,
value: Float,
onValueChangeFinished: (Float) -> Unit ,
titleTextColor: Color = MaterialTheme.colorScheme.onBackground,
disabledTextColor: Color = MaterialTheme.colorScheme.outline,
enabled: Boolean = true
) {
Column(
modifier = modifier
.wrapContentHeight()
.padding(all = 8.dp)
) {
val titleColor = if (enabled) titleTextColor else disabledTextColor
Text(text = titleText, style = MaterialTheme.typography.titleLarge, color = titleColor, fontSize = 20.sp)
var sliderValue by remember { mutableStateOf(value) }
Slider(
modifier = Modifier.fillMaxWidth().padding(start = 8.dp),
value = sliderValue,
onValueChange = { sliderValue = it },
enabled = enabled,
valueRange = 0f..100f,
onValueChangeFinished = {
onValueChangeFinished(sliderValue)
},
colors = SliderDefaults.colors(
thumbColor = MaterialTheme.colorScheme.primary,
activeTrackColor = MaterialTheme.colorScheme.primary
)
)
}
} }

View File

@@ -20,10 +20,7 @@ import androidx.compose.material.Card
import androidx.compose.material.OutlinedTextField import androidx.compose.material.OutlinedTextField
import androidx.compose.material.TextFieldDefaults import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Error import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
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,6 +62,7 @@ import androidx.core.text.HtmlCompat
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.compose.rememberAsyncImagePainter import coil.compose.rememberAsyncImagePainter
import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.FlowRow
import com.kieronquinn.monetcompat.core.MonetCompat
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.ui.theme.RatingSelected import com.owenlejeune.tvtime.ui.theme.RatingSelected
@@ -924,4 +922,40 @@ fun TimeoutSnackbar(
onDismiss() onDismiss()
} }
} }
}
@Composable
fun CenteredIconCircle(
size: Dp,
backgroundColor: Color,
iconTint: Color,
icon: ImageVector,
contentDescription: String?,
showIcon: Boolean = true,
onClick: () -> Unit = {}
) {
Box(
modifier = Modifier
.size(size)
.clip(CircleShape)
.background(color = backgroundColor)
.clickable {
onClick()
}
) {
if (showIcon) {
Column(
modifier = Modifier.size(size),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
imageVector = icon,
contentDescription = contentDescription,
modifier = Modifier.size(size / 2),
tint = iconTint
)
}
}
}
} }

View File

@@ -4,8 +4,8 @@ import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
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.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.ui.screens.tabs.bottom.AccountTabContent import com.owenlejeune.tvtime.ui.screens.main.AccountTabContent
import com.owenlejeune.tvtime.utils.ResourceUtils import com.owenlejeune.tvtime.utils.ResourceUtils
import com.owenlejeune.tvtime.utils.SessionManager import com.owenlejeune.tvtime.utils.SessionManager
import org.koin.core.component.inject import org.koin.core.component.inject

View File

@@ -5,8 +5,8 @@ import androidx.navigation.NavHostController
import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.api.tmdb.api.v3.HomePageService import com.owenlejeune.tvtime.api.tmdb.api.v3.HomePageService
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePageResponse import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePageResponse
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.ui.screens.tabs.bottom.MediaTabContent import com.owenlejeune.tvtime.ui.screens.main.MediaTabContent
import com.owenlejeune.tvtime.utils.ResourceUtils import com.owenlejeune.tvtime.utils.ResourceUtils
import org.koin.core.component.inject import org.koin.core.component.inject
import retrofit2.Response import retrofit2.Response

View File

@@ -9,11 +9,9 @@ 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.navArgument
import com.owenlejeune.tvtime.ui.screens.MediaDetailView import com.owenlejeune.tvtime.ui.screens.main.MediaDetailView
import com.owenlejeune.tvtime.ui.screens.MainAppView import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.*
import com.owenlejeune.tvtime.ui.screens.PersonDetailView
import com.owenlejeune.tvtime.ui.screens.tabs.bottom.*
object NavConstants { object NavConstants {
const val ID_KEY = "id_key" const val ID_KEY = "id_key"

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens.tabs.bottom package com.owenlejeune.tvtime.ui.screens.main
import android.content.Context import android.content.Context
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -12,7 +12,6 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -29,8 +28,7 @@ import com.owenlejeune.tvtime.ui.components.*
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.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.tabs.top.ScrollableTabs
import com.owenlejeune.tvtime.ui.screens.tabs.top.ScrollableTabs
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.CoroutineScope

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens package com.owenlejeune.tvtime.ui.screens.main
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens.tabs.bottom package com.owenlejeune.tvtime.ui.screens.main
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens package com.owenlejeune.tvtime.ui.screens.main
import androidx.compose.animation.rememberSplineBasedDecay import androidx.compose.animation.rememberSplineBasedDecay
import androidx.compose.foundation.Image import androidx.compose.foundation.Image

View File

@@ -1,17 +1,13 @@
package com.owenlejeune.tvtime.ui.screens package com.owenlejeune.tvtime.ui.screens.main
import android.content.Context import android.content.Context
import android.widget.Toast import android.widget.Toast
import androidx.compose.animation.* import androidx.compose.animation.*
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.keyframes
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.* import androidx.compose.foundation.*
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Send import androidx.compose.material.icons.filled.Send
@@ -29,7 +25,6 @@ import androidx.compose.ui.text.font.FontStyle
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
import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.IntSize
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

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens.tabs.bottom package com.owenlejeune.tvtime.ui.screens.main
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -16,8 +16,7 @@ import com.owenlejeune.tvtime.ui.components.PosterGrid
import com.owenlejeune.tvtime.ui.navigation.MainNavItem import com.owenlejeune.tvtime.ui.navigation.MainNavItem
import com.owenlejeune.tvtime.ui.navigation.MediaFetchFun import com.owenlejeune.tvtime.ui.navigation.MediaFetchFun
import com.owenlejeune.tvtime.ui.navigation.MediaTabNavItem import com.owenlejeune.tvtime.ui.navigation.MediaTabNavItem
import com.owenlejeune.tvtime.ui.screens.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.tabs.top.Tabs
import com.owenlejeune.tvtime.ui.screens.tabs.top.Tabs
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens package com.owenlejeune.tvtime.ui.screens.main
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens package com.owenlejeune.tvtime.ui.screens.main
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens.tabs.bottom 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
@@ -14,7 +14,6 @@ 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.ui.components.PeoplePosterGrid import com.owenlejeune.tvtime.ui.components.PeoplePosterGrid
import com.owenlejeune.tvtime.ui.navigation.MainNavItem import com.owenlejeune.tvtime.ui.navigation.MainNavItem
import com.owenlejeune.tvtime.ui.screens.MediaViewType
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@@ -0,0 +1,296 @@
package com.owenlejeune.tvtime.ui.screens.main
import android.os.Build
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.ResetTv
import androidx.compose.material.icons.filled.RestartAlt
import androidx.compose.material3.*
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.Icon
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.kieronquinn.monetcompat.core.MonetCompat
import com.owenlejeune.tvtime.BuildConfig
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.preferences.AppPreferences
import com.owenlejeune.tvtime.ui.components.*
import com.owenlejeune.tvtime.utils.SessionManager
import dev.kdrag0n.monet.factory.ColorSchemeFactory
import kotlinx.coroutines.launch
import org.koin.java.KoinJavaComponent.get
@Composable
fun SettingsTab(preferences: AppPreferences = get(AppPreferences::class.java)) {
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.fillMaxSize()
.padding(start = 8.dp, top = 8.dp, bottom = 8.dp, end = 24.dp)
.verticalScroll(scrollState)
) {
// val usePreferences = remember { mutableStateOf(preferences.usePreferences) }
// TopLevelSwitch(
// text = "Enable Preferences",
// checkedState = usePreferences,
// onCheckChanged = { isChecked ->
// usePreferences.value = isChecked
// preferences.usePreferences = isChecked
// }
// )
//
// Column(
// modifier = Modifier
// .fillMaxWidth()
// .wrapContentHeight()
// .padding(start = 8.dp, top = 8.dp, bottom = 8.dp, end = 24.dp)
// .verticalScroll()
// ) {
PreferenceHeading(text = stringResource(R.string.preference_heading_search))
val persistentSearch = remember { mutableStateOf(preferences.persistentSearch) }
SwitchPreference(
titleText = stringResource(R.string.preferences_persistent_search_title),
subtitleText = stringResource(R.string.preferences_persistent_search_subtitle),
checkState = persistentSearch.value,
onCheckedChange = { isChecked ->
persistentSearch.value = isChecked
preferences.persistentSearch = isChecked
}
)
val hideTitle = remember { mutableStateOf(preferences.hideTitle) }
SwitchPreference(
titleText = stringResource(R.string.preferences_hide_heading_title),
subtitleText = stringResource(R.string.preferences_hide_heading_subtitle),
checkState = hideTitle.value,
onCheckedChange = { isChecked ->
hideTitle.value = isChecked
preferences.hideTitle = isChecked
},
enabled = persistentSearch.value
)
PreferenceHeading(text = stringResource(id = R.string.preference_heading_design))
val isSystemColorsSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
if (isSystemColorsSupported) {
val useSystemColors = remember { mutableStateOf(preferences.useSystemColors) }
SwitchPreference(
titleText = stringResource(id = R.string.preference_system_colors_heading),
checkState = useSystemColors.value,
onCheckedChange = { isChecked ->
useSystemColors.value = isChecked
preferences.useSystemColors = isChecked
MonetCompat.useSystemColorsOnAndroid12 = isChecked
MonetCompat.getInstance().updateMonetColors()
}
)
}
SliderPreference(
titleText = stringResource(id = R.string.preference_chroma_factor_heading),
value = preferences.chromaMultiplier.toFloat() * 50f,
onValueChangeFinished = { value ->
with((value / 50f).toDouble()) {
preferences.chromaMultiplier = this
MonetCompat.chromaMultiplier = this
}
MonetCompat.getInstance().updateMonetColors()
},
enabled = if (isSystemColorsSupported) !preferences.useSystemColors else true
)
val showWallpaperPicker = remember { mutableStateOf(false) }
val wallpaperPickerModifier = if (isSystemColorsSupported && preferences.useSystemColors) {
Modifier
} else {
Modifier.clickable { showWallpaperPicker.value = true }
}
Text(
text = stringResource(id = R.string.preference_wallpaper_color_heading),
style = MaterialTheme.typography.titleLarge,
fontSize = 20.sp,
color = if (isSystemColorsSupported && preferences.useSystemColors) {
MaterialTheme.colorScheme.outline
} else {
MaterialTheme.colorScheme.onBackground
},
modifier = wallpaperPickerModifier
.padding(start = 8.dp)
)
if (showWallpaperPicker.value) {
WallpaperPicker(showPopup = showWallpaperPicker)
}
if (BuildConfig.DEBUG) {
Divider(modifier = Modifier.padding(start = 8.dp, top = 20.dp))
Text(
modifier = Modifier.padding(start = 8.dp, top = 20.dp),
text = stringResource(R.string.preferences_debug_title),
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.primary
)
DebugOptions()
}
}
}
@Composable
private fun WallpaperPicker(
showPopup: MutableState<Boolean>,
preference: AppPreferences = get(AppPreferences::class.java)
) {
val context = LocalContext.current
val wallpaperColors = remember { mutableStateOf<List<Int>>(emptyList()) }
val selectedWallpaperColor = remember { mutableStateOf(0) }
LaunchedEffect(true) {
val colors = MonetCompat.getInstance().getAvailableWallpaperColors() ?: emptyList()
if (colors.isEmpty()) {
Toast.makeText(context, context.getString(R.string.preference_no_wallpaper_colors), Toast.LENGTH_SHORT).show()
showPopup.value = false
}
wallpaperColors.value = colors
selectedWallpaperColor.value = MonetCompat.getInstance().getSelectedWallpaperColor() ?: 0
}
if (wallpaperColors.value.isNotEmpty() && selectedWallpaperColor.value != 0) {
val colorCircleSize = 50.dp
AlertDialog(
modifier = Modifier.padding(12.dp),
title = { Text(text = stringResource(id = R.string.preference_wallpaper_color_heading)) },
text = {
Row(
modifier = Modifier
.height(colorCircleSize)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
wallpaperColors.value.forEach {
CenteredIconCircle(
size = colorCircleSize,
backgroundColor = Color(it),
iconTint = Color.White,
icon = Icons.Filled.Check,
contentDescription = null,
showIcon = it == selectedWallpaperColor.value,
onClick = {
if (it != selectedWallpaperColor.value) {
preference.selectedColor = it
MonetCompat
.getInstance()
.updateMonetColors()
}
}
)
}
CenteredIconCircle(
size = colorCircleSize,
backgroundColor = MaterialTheme.colorScheme.secondaryContainer,
iconTint = MaterialTheme.colorScheme.onSecondaryContainer,
icon = Icons.Filled.RestartAlt,
contentDescription = null,
onClick = {
if (preference.selectedColor != Int.MAX_VALUE) {
preference.selectedColor = Int.MAX_VALUE
MonetCompat.getInstance().updateMonetColors()
} else {
Toast.makeText(context, context.getString(R.string.preference_wallpaper_color_default_selected), Toast.LENGTH_SHORT).show()
}
}
)
}
},
dismissButton = {
TextButton(onClick = { showPopup.value = false }) {
Text(text = stringResource(id = R.string.action_cancel))
}
},
confirmButton = {},
onDismissRequest = { showPopup.value = false }
)
}
}
@Composable
private fun DebugOptions(preferences: AppPreferences = get(AppPreferences::class.java)) {
val context = LocalContext.current
val firstLaunchTesting = remember { mutableStateOf(preferences.firstLaunchTesting) }
SwitchPreference(
titleText = "Always show onboarding flow",
subtitleText = "Show onboarding flow on each launch",
checkState = firstLaunchTesting.value,
onCheckedChange = { isChecked ->
firstLaunchTesting.value = isChecked
preferences.firstLaunchTesting = isChecked
}
)
val shouldShowPalette = remember { mutableStateOf(false) }
Text(
text = "Show material palette",
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 12.dp)
.clickable(
onClick = {
shouldShowPalette.value = true
}
)
)
if (shouldShowPalette.value) {
AlertDialog(
modifier = Modifier.padding(12.dp),
title = { Text(text = "Palette") },
text = { PaletteView() },
dismissButton = {
TextButton(onClick = { shouldShowPalette.value = false }) {
Text("Dismiss")
}
},
confirmButton = {},
onDismissRequest = { shouldShowPalette.value = false }
)
}
Text(
text = "Clear session",
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 12.dp)
.clickable(
onClick = {
preferences.guestSessionId = ""
SessionManager.clearSession {
Toast
.makeText(context, "Cleared session: $it", Toast.LENGTH_SHORT)
.show()
}
}
)
)
}

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens.tabs.top package com.owenlejeune.tvtime.ui.screens.main.tabs.top
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer

View File

@@ -0,0 +1,28 @@
package com.owenlejeune.tvtime.ui.screens.onboarding
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Composable
import com.owenlejeune.tvtime.R
sealed class OnboardingPage(
@StringRes val title: Int,
@StringRes val description: Int,
@DrawableRes val image: Int? = null,
val additionalContent: @Composable (AppCompatActivity) -> Unit = {}
) {
companion object {
fun values() = listOf(ExamplePage)
operator fun get(i: Int) = values()[i]
}
object ExamplePage: OnboardingPage(
R.string.app_name,
R.string.example_page_desc,
R.drawable.ic_launcher_foreground
)
}

View File

@@ -1,145 +0,0 @@
package com.owenlejeune.tvtime.ui.screens.tabs.bottom
import android.widget.Toast
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Switch
import androidx.compose.material.TextButton
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import com.owenlejeune.tvtime.BuildConfig
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.di.modules.preferencesModule
import com.owenlejeune.tvtime.preferences.AppPreferences
import com.owenlejeune.tvtime.ui.components.*
import com.owenlejeune.tvtime.utils.SessionManager
import org.koin.java.KoinJavaComponent.get
@Composable
fun SettingsTab(preferences: AppPreferences = get(AppPreferences::class.java)) {
val scrollState = rememberScrollState()
Column(modifier = Modifier
.fillMaxSize()
.padding(start = 8.dp, top = 8.dp, bottom = 8.dp, end = 24.dp)
.verticalScroll(scrollState)
) {
// val usePreferences = remember { mutableStateOf(preferences.usePreferences) }
// TopLevelSwitch(
// text = "Enable Preferences",
// checkedState = usePreferences,
// onCheckChanged = { isChecked ->
// usePreferences.value = isChecked
// preferences.usePreferences = isChecked
// }
// )
//
// Column(
// modifier = Modifier
// .fillMaxWidth()
// .wrapContentHeight()
// .padding(start = 8.dp, top = 8.dp, bottom = 8.dp, end = 24.dp)
// .verticalScroll()
// ) {
PreferenceHeading(text = stringResource(R.string.preference_heading_search))
val persistentSearch = remember { mutableStateOf(preferences.persistentSearch) }
SwitchPreference(
titleText = stringResource(R.string.preferences_persistent_search_title),
subtitleText = stringResource(R.string.preferences_persistent_search_subtitle),
checkState = persistentSearch.value,
onCheckedChange = { isChecked ->
persistentSearch.value = isChecked
preferences.persistentSearch = isChecked
}
)
val hideTitle = remember { mutableStateOf(preferences.hideTitle) }
SwitchPreference(
titleText = stringResource(R.string.preferences_hide_heading_title),
subtitleText = stringResource(R.string.preferences_hide_heading_subtitle),
checkState = hideTitle.value,
onCheckedChange = { isChecked ->
hideTitle.value = isChecked
preferences.hideTitle = isChecked
},
enabled = persistentSearch.value
)
if (BuildConfig.DEBUG) {
Text(
modifier = Modifier.padding(start = 8.dp, top = 20.dp),
text = stringResource(R.string.preferences_debug_title),
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onBackground
)
DebugOptions()
}
}
}
@Composable
private fun DebugOptions(preferences: AppPreferences = get(AppPreferences::class.java)) {
val context = LocalContext.current
val shouldShowPalette = remember { mutableStateOf(false) }
Text(
text = "Show material palette",
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 12.dp)
.clickable(
onClick = {
shouldShowPalette.value = true
}
)
)
if (shouldShowPalette.value) {
PaletteDialog(shouldShowPalette)
}
Text(
text = "Clear session",
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 12.dp)
.clickable(
onClick = {
preferences.guestSessionId = ""
SessionManager.clearSession {
Toast.makeText(context, "Cleared session: $it", Toast.LENGTH_SHORT).show()
}
}
)
)
}
@Composable
private fun PaletteDialog(showDialog: MutableState<Boolean>) {
AlertDialog(
modifier = Modifier.padding(12.dp),
title = { Text(text = "Palette") },
text = { PaletteView() },
dismissButton = {
TextButton(onClick = { showDialog.value = false }) {
Text("Dismiss")
}
},
confirmButton = {},
onDismissRequest = { showDialog.value = false }
)
}

View File

@@ -54,6 +54,12 @@
<string name="preferences_hide_heading_title">Expanded search bar</string> <string name="preferences_hide_heading_title">Expanded search bar</string>
<string name="preferences_hide_heading_subtitle">Keep search bar expanded at all times</string> <string name="preferences_hide_heading_subtitle">Keep search bar expanded at all times</string>
<string name="preferences_debug_title">Developer options</string> <string name="preferences_debug_title">Developer options</string>
<string name="preference_heading_design">Design</string>
<string name="preference_system_colors_heading">Use system colors</string>
<string name="preference_chroma_factor_heading">Saturation</string>
<string name="preference_wallpaper_color_heading">Wallpaper Color Picker</string>
<string name="preference_no_wallpaper_colors">Unavailable for the current wallpaper or your current settings</string>
<string name="preference_wallpaper_color_default_selected">Wallpaper color already set to default</string>
<!-- video type --> <!-- video type -->
<string name="video_type_clip">Clips</string> <string name="video_type_clip">Clips</string>
@@ -104,4 +110,8 @@
<!-- intent routes --> <!-- intent routes -->
<string name="intent_route_auth_return">tvtime.auth.return</string> <string name="intent_route_auth_return">tvtime.auth.return</string>
<!-- onboarding -->
<string name="get_started">Get started</string>
<string name="example_page_desc">This is an example</string>
</resources> </resources>