Milan Jurkulak 3 лет назад
Родитель
Сommit
fafe5952c7

+ 4 - 10
app/src/main/kotlin/org/mjdev/tvapp/base/activity/ComposableActivity.kt

@@ -30,7 +30,6 @@ import androidx.tv.material3.Surface
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.extensions.ContextExt.isATv
 import org.mjdev.tvapp.base.extensions.ContextExt.isATv
 import org.mjdev.tvapp.base.extensions.NavExt.rememberNavControllerEx
 import org.mjdev.tvapp.base.extensions.NavExt.rememberNavControllerEx
-import org.mjdev.tvapp.base.ui.components.complex.ScreenView
 import org.mjdev.tvapp.base.ui.components.complex.EmptyScreen
 import org.mjdev.tvapp.base.ui.components.complex.EmptyScreen
 import org.mjdev.tvapp.base.ui.components.navigation.NavGraphBuilderEx
 import org.mjdev.tvapp.base.ui.components.navigation.NavGraphBuilderEx
 import org.mjdev.tvapp.base.ui.components.complex.NavHostEx
 import org.mjdev.tvapp.base.ui.components.complex.NavHostEx
@@ -69,16 +68,11 @@ open class ComposableActivity : ComponentActivity() {
                     containerColor = backgroundColor
                     containerColor = backgroundColor
                 )
                 )
             ) {
             ) {
-                ScreenView(
+                NavHostEx(
+                    modifier = Modifier.fillMaxSize(),
                     navController = navController,
                     navController = navController,
-                    backgroundColor = backgroundColor
-                ) {
-                    NavHostEx(
-                        modifier = Modifier.fillMaxSize(),
-                        navController = navController,
-                        builder = navGraphBuilder
-                    )
-                }
+                    builder = navGraphBuilder
+                )
             }
             }
         }
         }
 
 

+ 20 - 18
app/src/main/kotlin/org/mjdev/tvapp/base/ui/components/complex/NavHostEx.kt

@@ -49,23 +49,25 @@ fun NavHostEx(
         screen(route = EmptyScreen())
         screen(route = EmptyScreen())
     }
     }
 ) {
 ) {
-
-    NavHost(
-        modifier = modifier,
+    ScreenView(
         navController = navController,
         navController = navController,
-        graph = remember(route, builder) {
-            navController.createGraph(
-                navController,
-                route,
-                startRoute,
-                builder
-            )
-        },
-        contentAlignment = contentAlignment,
-        enterTransition = enterTransition,
-        exitTransition = exitTransition,
-        popEnterTransition = popEnterTransition,
-        popExitTransition = popExitTransition,
-    )
-
+    ) {
+        NavHost(
+            modifier = modifier,
+            navController = navController,
+            graph = remember(route, builder) {
+                navController.createGraph(
+                    navController,
+                    route,
+                    startRoute,
+                    builder
+                )
+            },
+            contentAlignment = contentAlignment,
+            enterTransition = enterTransition,
+            exitTransition = exitTransition,
+            popEnterTransition = popEnterTransition,
+            popExitTransition = popExitTransition,
+        )
+    }
 }
 }

+ 1 - 13
app/src/main/kotlin/org/mjdev/tvapp/base/ui/components/complex/ScreenView.kt

@@ -8,17 +8,11 @@
 
 
 package org.mjdev.tvapp.base.ui.components.complex
 package org.mjdev.tvapp.base.ui.components.complex
 
 
-import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
 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.Shape
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.extensions.NavExt.rememberNavControllerEx
 import org.mjdev.tvapp.base.extensions.NavExt.rememberNavControllerEx
 import org.mjdev.tvapp.base.ui.components.navigation.NavHostControllerEx
 import org.mjdev.tvapp.base.ui.components.navigation.NavHostControllerEx
@@ -28,21 +22,15 @@ import org.mjdev.tvapp.base.ui.components.navigation.NavHostControllerEx
 @Composable
 @Composable
 fun ScreenView(
 fun ScreenView(
     navController: NavHostControllerEx = rememberNavControllerEx(),
     navController: NavHostControllerEx = rememberNavControllerEx(),
-    backgroundColor: Color = Color.DarkGray,
-    roundCornerSize: Dp = 16.dp,
-    shape: Shape = RoundedCornerShape(roundCornerSize),
     content: @Composable () -> Unit = { EmptyScreen() }
     content: @Composable () -> Unit = { EmptyScreen() }
 ) {
 ) {
     Box(
     Box(
-        modifier = Modifier
-            .fillMaxSize()
-            .background(backgroundColor, shape),
+        modifier = Modifier.fillMaxSize(),
         contentAlignment = Alignment.BottomCenter,
         contentAlignment = Alignment.BottomCenter,
     ) {
     ) {
         if (navController.menuState.value) {
         if (navController.menuState.value) {
             Navigation(
             Navigation(
                 navController = navController,
                 navController = navController,
-                backgroundColor = backgroundColor,
                 content = {
                 content = {
                     content()
                     content()
                 }
                 }

+ 48 - 27
app/src/main/kotlin/org/mjdev/tvapp/base/ui/components/navigation/Screen.kt

@@ -10,7 +10,6 @@
 
 
 package org.mjdev.tvapp.base.ui.components.navigation
 package org.mjdev.tvapp.base.ui.components.navigation
 
 
-import android.os.Bundle
 import androidx.annotation.CallSuper
 import androidx.annotation.CallSuper
 import androidx.annotation.MainThread
 import androidx.annotation.MainThread
 import androidx.compose.animation.AnimatedContentTransitionScope
 import androidx.compose.animation.AnimatedContentTransitionScope
@@ -33,11 +32,10 @@ import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.unit.sp
 import androidx.navigation.NamedNavArgument
 import androidx.navigation.NamedNavArgument
 import androidx.navigation.NavBackStackEntry
 import androidx.navigation.NavBackStackEntry
-import androidx.navigation.NavController
 import androidx.navigation.NavDeepLink
 import androidx.navigation.NavDeepLink
-import androidx.navigation.NavDestination
 import androidx.navigation.NavHostController
 import androidx.navigation.NavHostController
 import androidx.navigation.compose.composable
 import androidx.navigation.compose.composable
+import androidx.navigation.navOptions
 import androidx.tv.material3.Text
 import androidx.tv.material3.Text
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.extensions.AnimExt.FadeIn
 import org.mjdev.tvapp.base.extensions.AnimExt.FadeIn
@@ -46,7 +44,7 @@ import org.mjdev.tvapp.base.extensions.NavExt.rememberNavControllerEx
 import kotlin.reflect.full.createInstance
 import kotlin.reflect.full.createInstance
 
 
 @Suppress("unused", "LeakingThis")
 @Suppress("unused", "LeakingThis")
-open class Screen : NavController.OnDestinationChangedListener {
+open class Screen {
 
 
     private val routeBase: String
     private val routeBase: String
         get() = (this::class.simpleName ?: "-") + "?"
         get() = (this::class.simpleName ?: "-") + "?"
@@ -54,13 +52,13 @@ open class Screen : NavController.OnDestinationChangedListener {
     open val completeRoute: String
     open val completeRoute: String
         get() = routeBase.let { rb ->
         get() = routeBase.let { rb ->
             var routeImpl = rb
             var routeImpl = rb
-            args.forEach { arg ->
+            pageArgs.forEach { arg ->
                 routeImpl = routeImpl.plus("${arg.name}={${arg.name}}")
                 routeImpl = routeImpl.plus("${arg.name}={${arg.name}}")
             }
             }
             routeImpl
             routeImpl
         }
         }
 
 
-    open val args: List<NamedNavArgument> = emptyList()
+    open val pageArgs: List<NamedNavArgument> = emptyList()
 
 
     open val title: Any? = null
     open val title: Any? = null
 
 
@@ -85,18 +83,20 @@ open class Screen : NavController.OnDestinationChangedListener {
             menuRoute = completeRoute
             menuRoute = completeRoute
         ) else null
         ) else null
 
 
+    lateinit var navController: NavHostControllerEx
+    var backStackEntry: NavBackStackEntry? = null
+    lateinit var args: Map<String, Any?>
+
     @TvPreview
     @TvPreview
     @Composable
     @Composable
     @CallSuper
     @CallSuper
     open fun Compose() {
     open fun Compose() {
         val navController = rememberNavControllerEx()
         val navController = rememberNavControllerEx()
-
         Compose(
         Compose(
             navController,
             navController,
             null,
             null,
             mapOf()
             mapOf()
         )
         )
-
     }
     }
 
 
     @Composable
     @Composable
@@ -105,13 +105,15 @@ open class Screen : NavController.OnDestinationChangedListener {
         backStackEntry: NavBackStackEntry?,
         backStackEntry: NavBackStackEntry?,
         args: Map<String, Any?>
         args: Map<String, Any?>
     ) {
     ) {
-
-        if (showOnce) {
-            navController.addOnDestinationChangedListener(this)
-        }
-
+        this.navController = navController
+        this.backStackEntry = backStackEntry
+        this.args = args
         navController.menuState.value = !immersive
         navController.menuState.value = !immersive
+        ComposeScreen()
+    }
 
 
+    @Composable
+    open fun ComposeScreen() {
         Column(
         Column(
             modifier = Modifier
             modifier = Modifier
                 .fillMaxSize()
                 .fillMaxSize()
@@ -126,17 +128,6 @@ open class Screen : NavController.OnDestinationChangedListener {
                 color = Color.White
                 color = Color.White
             )
             )
         }
         }
-
-    }
-
-    override fun onDestinationChanged(
-        controller: NavController,
-        destination: NavDestination,
-        arguments: Bundle?
-    ) {
-        if (showOnce && destination.route == completeRoute) {
-            controller.popBackStack()
-        }
     }
     }
 
 
     companion object {
     companion object {
@@ -160,6 +151,36 @@ open class Screen : NavController.OnDestinationChangedListener {
             }
             }
         }
         }
 
 
+        @MainThread
+        inline fun <reified T : Screen> NavHostController.openAsTop(
+            vararg values: Any?
+        ) {
+            val instance = T::class.createInstance()
+            instance.completeRoute.let { r ->
+                var routeImpl = r
+                instance.pageArgs.forEachIndexed { idx, arg ->
+                    routeImpl = routeImpl.replace(
+                        "{${arg.name}}",
+                        (values[idx] ?: "").toString()
+                    )
+                }
+                routeImpl
+            }.also { finalRoute ->
+                val currentRoute = currentRoute
+                val equals = currentRoute?.equals(finalRoute)
+                if (equals != true) {
+                    navigate(
+                        finalRoute,
+                        navOptions {
+                            popBackStack()
+                            launchSingleTop = true
+                        },
+                        null
+                    )
+                }
+            }
+        }
+
         @MainThread
         @MainThread
         inline fun <reified T : Screen> NavHostController.open(
         inline fun <reified T : Screen> NavHostController.open(
             vararg values: Any?
             vararg values: Any?
@@ -167,7 +188,7 @@ open class Screen : NavController.OnDestinationChangedListener {
             val instance = T::class.createInstance()
             val instance = T::class.createInstance()
             instance.completeRoute.let { r ->
             instance.completeRoute.let { r ->
                 var routeImpl = r
                 var routeImpl = r
-                instance.args.forEachIndexed { idx, arg ->
+                instance.pageArgs.forEachIndexed { idx, arg ->
                     routeImpl = routeImpl.replace(
                     routeImpl = routeImpl.replace(
                         "{${arg.name}}",
                         "{${arg.name}}",
                         (values[idx] ?: "").toString()
                         (values[idx] ?: "").toString()
@@ -217,7 +238,7 @@ open class Screen : NavController.OnDestinationChangedListener {
             }
             }
             composable(
             composable(
                 route = route.completeRoute,
                 route = route.completeRoute,
-                arguments = route.args,
+                arguments = route.pageArgs,
                 enterTransition = enterTransition,
                 enterTransition = enterTransition,
                 exitTransition = exitTransition,
                 exitTransition = exitTransition,
                 deepLinks = deepLinks,
                 deepLinks = deepLinks,
@@ -225,7 +246,7 @@ open class Screen : NavController.OnDestinationChangedListener {
                 popExitTransition = popExitTransition
                 popExitTransition = popExitTransition
             ) { be ->
             ) { be ->
                 val rArgs = mutableMapOf<String, Any?>().apply {
                 val rArgs = mutableMapOf<String, Any?>().apply {
-                    route.args.forEach { arg ->
+                    route.pageArgs.forEach { arg ->
                         put(arg.name, be.arg(arg.name, null))
                         put(arg.name, be.arg(arg.name, null))
                     }
                     }
                 }
                 }

+ 1 - 9
app/src/main/kotlin/org/mjdev/tvapp/base/ui/components/navigation/ScreenWithPages.kt

@@ -9,7 +9,6 @@
 package org.mjdev.tvapp.base.ui.components.navigation
 package org.mjdev.tvapp.base.ui.components.navigation
 
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
-import androidx.navigation.NavBackStackEntry
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.ui.components.page.TvPager
 import org.mjdev.tvapp.base.ui.components.page.TvPager
 import org.mjdev.tvapp.base.ui.components.page.PagerScope
 import org.mjdev.tvapp.base.ui.components.page.PagerScope
@@ -22,14 +21,7 @@ open class ScreenWithPages : Screen() {
 
 
     @TvPreview
     @TvPreview
     @Composable
     @Composable
-    override fun Compose() = super.Compose()
-
-    @Composable
-    override fun Compose(
-        navController: NavHostControllerEx,
-        backStackEntry: NavBackStackEntry?,
-        args: Map<String, Any?>
-    ) {
+    override fun ComposeScreen() {
         TvPager(
         TvPager(
             navController = navController,
             navController = navController,
             startIndex = startPageIndex,
             startIndex = startPageIndex,

+ 2 - 11
app/src/main/kotlin/org/mjdev/tvapp/ui/screens/DetailScreen.kt

@@ -14,14 +14,12 @@ import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.remember
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.navigation.NavBackStackEntry
 import androidx.navigation.NavType
 import androidx.navigation.NavType
 import androidx.navigation.navArgument
 import androidx.navigation.navArgument
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.extensions.HiltExt.appViewModel
 import org.mjdev.tvapp.base.extensions.HiltExt.appViewModel
 import org.mjdev.tvapp.base.extensions.StringExt.asException
 import org.mjdev.tvapp.base.extensions.StringExt.asException
-import org.mjdev.tvapp.base.ui.components.navigation.NavHostControllerEx
 import org.mjdev.tvapp.base.ui.components.navigation.Screen
 import org.mjdev.tvapp.base.ui.components.navigation.Screen
 import org.mjdev.tvapp.state.DetailsLoadingState
 import org.mjdev.tvapp.state.DetailsLoadingState
 import org.mjdev.tvapp.ui.components.Details
 import org.mjdev.tvapp.ui.components.Details
@@ -38,7 +36,7 @@ class DetailScreen : Screen() {
 
 
     override val immersive: Boolean = true
     override val immersive: Boolean = true
 
 
-    override val args = listOf(
+    override val pageArgs = listOf(
         navArgument(movieId) {
         navArgument(movieId) {
             defaultValue = -1
             defaultValue = -1
             type = NavType.LongType
             type = NavType.LongType
@@ -47,14 +45,7 @@ class DetailScreen : Screen() {
 
 
     @TvPreview
     @TvPreview
     @Composable
     @Composable
-    override fun Compose() = super.Compose()
-
-    @Composable
-    override fun Compose(
-        navController: NavHostControllerEx,
-        backStackEntry: NavBackStackEntry?,
-        args: Map<String, Any?>
-    ) {
+    override fun ComposeScreen() {
 
 
         val viewModel: DetailViewModel = appViewModel { context ->
         val viewModel: DetailViewModel = appViewModel { context ->
             DetailViewModel.mockDetailViewModel(context)
             DetailViewModel.mockDetailViewModel(context)

+ 0 - 1
app/src/main/kotlin/org/mjdev/tvapp/ui/screens/MainScreen.kt

@@ -27,7 +27,6 @@ class MainScreen : ScreenWithPages() {
 
 
     override val title: Int = R.string.app_name
     override val title: Int = R.string.app_name
     override val menuIcon: ImageVector = Icons.Default.Home
     override val menuIcon: ImageVector = Icons.Default.Home
-    override val startPageIndex: Int = 2
     override val immersive: Boolean = false
     override val immersive: Boolean = false
 
 
     @TvPreview
     @TvPreview

+ 2 - 11
app/src/main/kotlin/org/mjdev/tvapp/ui/screens/PlayerScreen.kt

@@ -16,7 +16,6 @@ import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.navigation.NavBackStackEntry
 import androidx.navigation.NavType
 import androidx.navigation.NavType
 import androidx.navigation.navArgument
 import androidx.navigation.navArgument
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.R
@@ -24,7 +23,6 @@ import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.extensions.ComposeExt.isEditMode
 import org.mjdev.tvapp.base.extensions.ComposeExt.isEditMode
 import org.mjdev.tvapp.base.extensions.HiltExt.appViewModel
 import org.mjdev.tvapp.base.extensions.HiltExt.appViewModel
 import org.mjdev.tvapp.base.extensions.StringExt.asException
 import org.mjdev.tvapp.base.extensions.StringExt.asException
-import org.mjdev.tvapp.base.ui.components.navigation.NavHostControllerEx
 import org.mjdev.tvapp.base.ui.components.navigation.Screen
 import org.mjdev.tvapp.base.ui.components.navigation.Screen
 import org.mjdev.tvapp.state.DetailsLoadingState
 import org.mjdev.tvapp.state.DetailsLoadingState
 import org.mjdev.tvapp.ui.components.Loading
 import org.mjdev.tvapp.ui.components.Loading
@@ -39,7 +37,7 @@ class PlayerScreen : Screen() {
     override val menuIcon: ImageVector get() = Icons.Filled.PlayArrow
     override val menuIcon: ImageVector get() = Icons.Filled.PlayArrow
     override val immersive: Boolean = true
     override val immersive: Boolean = true
 
 
-    override val args = listOf(
+    override val pageArgs = listOf(
         navArgument(movieId) {
         navArgument(movieId) {
             defaultValue = -1
             defaultValue = -1
             type = NavType.LongType
             type = NavType.LongType
@@ -48,14 +46,7 @@ class PlayerScreen : Screen() {
 
 
     @TvPreview
     @TvPreview
     @Composable
     @Composable
-    override fun Compose() = super.Compose()
-
-    @Composable
-    override fun Compose(
-        navController: NavHostControllerEx,
-        backStackEntry: NavBackStackEntry?,
-        args: Map<String, Any?>
-    ) {
+    override fun ComposeScreen() {
 
 
         val isEdit = isEditMode()
         val isEdit = isEditMode()
 
 

+ 14 - 24
app/src/main/kotlin/org/mjdev/tvapp/ui/screens/SplashScreen.kt

@@ -29,13 +29,11 @@ import androidx.compose.ui.graphics.Shadow
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.font.FontWeight
 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.NavBackStackEntry
 import androidx.tv.material3.MaterialTheme
 import androidx.tv.material3.MaterialTheme
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.delay
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.extensions.ComposeExt.isEditMode
 import org.mjdev.tvapp.base.extensions.ComposeExt.isEditMode
-import org.mjdev.tvapp.base.ui.components.navigation.NavHostControllerEx
 import org.mjdev.tvapp.base.ui.components.navigation.Screen
 import org.mjdev.tvapp.base.ui.components.navigation.Screen
 import org.mjdev.tvapp.base.ui.components.icon.IconAny
 import org.mjdev.tvapp.base.ui.components.icon.IconAny
 import org.mjdev.tvapp.base.ui.components.text.TextAny
 import org.mjdev.tvapp.base.ui.components.text.TextAny
@@ -48,32 +46,11 @@ class SplashScreen : Screen() {
 
 
     @TvPreview
     @TvPreview
     @Composable
     @Composable
-    override fun Compose() = super.Compose()
-
-    @Composable
-    override fun Compose(
-        navController: NavHostControllerEx,
-        backStackEntry: NavBackStackEntry?,
-        args: Map<String, Any?>
-    ) {
+    override fun ComposeScreen() {
 
 
         val isEdit = isEditMode()
         val isEdit = isEditMode()
         val scale = remember { Animatable(if (isEdit) 1f else 0f) }
         val scale = remember { Animatable(if (isEdit) 1f else 0f) }
 
 
-        LaunchedEffect(key1 = true) {
-            scale.animateTo(
-                targetValue = 0.7f,
-                animationSpec = tween(
-                    durationMillis = 800,
-                    easing = {
-                        OvershootInterpolator(4f).getInterpolation(it)
-                    })
-            )
-            delay(3000L)
-            navController.addOnDestinationChangedListener(this@SplashScreen)
-            navController.open<MainScreen>()
-        }
-
         Box(
         Box(
             modifier = Modifier
             modifier = Modifier
                 .scale(scale.value)
                 .scale(scale.value)
@@ -119,6 +96,19 @@ class SplashScreen : Screen() {
             )
             )
         }
         }
 
 
+        LaunchedEffect(key1 = true) {
+            scale.animateTo(
+                targetValue = 0.7f,
+                animationSpec = tween(
+                    durationMillis = 800,
+                    easing = {
+                        OvershootInterpolator(4f).getInterpolation(it)
+                    })
+            )
+            delay(3000L)
+            navController.openAsTop<MainScreen>()
+        }
+
     }
     }
 
 
 }
 }