1
1
package io.github.reactivecircus.kstreamlined.android
2
2
3
+ import android.content.Context
3
4
import android.os.Bundle
5
+ import android.provider.Settings
4
6
import androidx.activity.ComponentActivity
7
+ import androidx.activity.compose.BackHandler
5
8
import androidx.activity.compose.setContent
6
9
import androidx.activity.enableEdgeToEdge
7
- import androidx.compose.animation.core.EaseInOutQuart
8
- import androidx.compose.animation.core.tween
9
- import androidx.compose.foundation.ExperimentalFoundationApi
10
+ import androidx.compose.animation.AnimatedContent
10
11
import androidx.compose.foundation.background
11
12
import androidx.compose.foundation.isSystemInDarkTheme
12
- import androidx.compose.foundation.layout.Box
13
13
import androidx.compose.foundation.layout.fillMaxSize
14
- import androidx.compose.foundation.layout.navigationBarsPadding
15
- import androidx.compose.foundation.layout.padding
16
- import androidx.compose.foundation.pager.HorizontalPager
17
- import androidx.compose.foundation.pager.PagerState
18
- import androidx.compose.foundation.pager.rememberPagerState
19
14
import androidx.compose.runtime.LaunchedEffect
20
15
import androidx.compose.runtime.getValue
21
16
import androidx.compose.runtime.mutableStateOf
22
17
import androidx.compose.runtime.saveable.rememberSaveable
23
18
import androidx.compose.runtime.setValue
24
19
import androidx.compose.ui.Alignment
25
20
import androidx.compose.ui.Modifier
26
- import androidx.compose.ui.graphics.graphicsLayer
21
+ import androidx.compose.ui.graphics.Color
27
22
import androidx.compose.ui.graphics.toArgb
28
- import androidx.compose.ui.unit.dp
29
- import androidx.compose.ui.util.lerp
23
+ import androidx.compose.ui.platform.LocalContext
30
24
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
31
25
import dagger.hilt.android.AndroidEntryPoint
32
- import io.github.reactivecircus.kstreamlined.android.designsystem.component.NavigationIsland
33
- import io.github.reactivecircus.kstreamlined.android.designsystem.component.NavigationIslandDivider
34
- import io.github.reactivecircus.kstreamlined.android.designsystem.component.NavigationIslandItem
35
26
import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.KSTheme
36
- import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.icon.Bookmarks
37
- import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.icon.KSIcons
38
- import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.icon.Kotlin
39
- import io.github.reactivecircus.kstreamlined.android.feature.home.HomeScreen
40
- import io.github.reactivecircus.kstreamlined.android.feature.savedforlater.SavedForLaterScreen
41
- import kotlin.math.absoluteValue
27
+ import io.github.reactivecircus.kstreamlined.android.feature.contentviewer.ContentViewerScreen
42
28
43
29
@AndroidEntryPoint
44
30
class KSActivity : ComponentActivity () {
45
31
46
- @OptIn(ExperimentalFoundationApi ::class )
47
32
override fun onCreate (savedInstanceState : Bundle ? ) {
48
33
installSplashScreen()
49
34
@@ -54,94 +39,68 @@ class KSActivity : ComponentActivity() {
54
39
setContent {
55
40
KSTheme {
56
41
val darkTheme = isSystemInDarkTheme()
57
- val navigationBarColor = KSTheme .colorScheme.background.toArgb()
58
- LaunchedEffect (darkTheme) {
59
- window.navigationBarColor = navigationBarColor
42
+ val context = LocalContext .current
43
+ val backgroundColor = KSTheme .colorScheme.background
44
+ LaunchedEffect (darkTheme, context) {
45
+ val navigationBarColor = when (SystemNavigationMode .of(context)) {
46
+ SystemNavigationMode .Gesture -> Color .Transparent
47
+ else -> backgroundColor
48
+ }
49
+ window.navigationBarColor = navigationBarColor.toArgb()
60
50
}
61
51
62
- Box (
52
+ var navDestination by rememberSaveable { mutableStateOf(NavDestination .Main ) }
53
+
54
+ AnimatedContent (
55
+ navDestination,
63
56
modifier = Modifier
64
57
.fillMaxSize()
65
- .background(KSTheme .colorScheme.background)
58
+ .background(KSTheme .colorScheme.background),
59
+ contentAlignment = Alignment .Center ,
60
+ label = " NavTransition" ,
66
61
) {
67
- var selectedNavItem by rememberSaveable { mutableStateOf(NavItemKey .Home ) }
68
-
69
- val pagerState = rememberPagerState(pageCount = { NavItemKey .entries.size })
70
- HorizontalPager (
71
- state = pagerState,
72
- modifier = Modifier .fillMaxSize(),
73
- beyondBoundsPageCount = NavItemKey .entries.size,
74
- userScrollEnabled = false ,
75
- ) {
76
- when (it) {
77
- NavItemKey .Home .ordinal -> {
78
- HomeScreen (
79
- modifier = Modifier .pagerScaleTransition(it, pagerState)
80
- )
81
- }
82
-
83
- NavItemKey .Saved .ordinal -> {
84
- SavedForLaterScreen (
85
- modifier = Modifier .pagerScaleTransition(it, pagerState)
86
- )
87
- }
62
+ when (it) {
63
+ NavDestination .Main -> {
64
+ MainScreen (
65
+ onViewContent = {
66
+ navDestination = NavDestination .ContentViewer
67
+ },
68
+ )
88
69
}
89
- }
90
70
91
- LaunchedEffect (selectedNavItem) {
92
- pagerState.animateScrollToPage(
93
- page = selectedNavItem.ordinal,
94
- animationSpec = tween(
95
- durationMillis = 400 ,
96
- easing = EaseInOutQuart ,
97
- ),
98
- )
71
+ NavDestination .ContentViewer -> {
72
+ ContentViewerScreen (
73
+ onNavigateUp = {
74
+ navDestination = NavDestination .Main
75
+ },
76
+ )
77
+ }
99
78
}
79
+ }
100
80
101
- NavigationIsland (
102
- modifier = Modifier
103
- .navigationBarsPadding()
104
- .padding(8 .dp)
105
- .align(Alignment .BottomCenter ),
106
- ) {
107
- NavigationIslandItem (
108
- selected = selectedNavItem == NavItemKey .Home ,
109
- icon = KSIcons .Kotlin ,
110
- contentDescription = " Home" ,
111
- onClick = {
112
- selectedNavItem = NavItemKey .Home
113
- },
114
- )
115
- NavigationIslandDivider ()
116
- NavigationIslandItem (
117
- selected = selectedNavItem == NavItemKey .Saved ,
118
- icon = KSIcons .Bookmarks ,
119
- contentDescription = " Saved" ,
120
- onClick = {
121
- selectedNavItem = NavItemKey .Saved
122
- },
123
- )
81
+ BackHandler (enabled = navDestination != NavDestination .Main ) {
82
+ if (navDestination != NavDestination .Main ) {
83
+ navDestination = NavDestination .Main
124
84
}
125
85
}
126
86
}
127
87
}
128
88
}
129
89
}
130
90
131
- @OptIn(ExperimentalFoundationApi ::class )
132
- private fun Modifier.pagerScaleTransition (page : Int , pagerState : PagerState ) = graphicsLayer {
133
- val pageOffset = (pagerState.currentPage - page) + pagerState.currentPageOffsetFraction
134
- lerp(
135
- start = 0.8f ,
136
- stop = 1f ,
137
- fraction = 1f - pageOffset.absoluteValue.coerceIn(0f , 1f ),
138
- ).also { scale ->
139
- scaleX = scale
140
- scaleY = scale
141
- }
91
+ enum class NavDestination {
92
+ Main ,
93
+ ContentViewer ,
142
94
}
143
95
144
- enum class NavItemKey {
145
- Home ,
146
- Saved ,
96
+ enum class SystemNavigationMode {
97
+ ThreeButton ,
98
+ TwoButton ,
99
+ Gesture ;
100
+
101
+ companion object {
102
+ fun of (context : Context ) = entries.getOrNull(
103
+ Settings .Secure .getInt(context.contentResolver, " navigation_mode" , - 1 )
104
+ )
105
+ }
147
106
}
0 commit comments