Skip to content

Commit e6e3665

Browse files
authored
Merge pull request #5 from Velord/feature/BackHandling
Feature/back handling
2 parents 2ee2f86 + e6db258 commit e6e3665

19 files changed

Lines changed: 168 additions & 104 deletions

File tree

app/build.gradle.kts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ plugins {
22
id(libs.plugins.android.application.get().pluginId)
33
id(libs.plugins.kotlin.android.get().pluginId)
44
id(libs.plugins.kotlin.kapt.get().pluginId)
5+
id(libs.plugins.dagger.hilt.get().pluginId)
56
}
67

78
// When app incompatible with previous version change this value
89
val globalVersion = 0
910
// When you create huge feature(or many) release change this value
10-
val majorVersion = 3
11+
val majorVersion = 5
1112
// When you create feature release change this value
1213
val minorVersion = 0
1314
// When you create fix change this value
@@ -104,10 +105,15 @@ dependencies {
104105
// Templates
105106
implementation(libs.bundles.kotlin.module)
106107
implementation(libs.bundles.androidx.module)
108+
implementation(libs.bundles.androidx.activity)
107109
// Compose
108110
implementation(libs.bundles.compose.material.third)
109111
implementation(libs.bundles.compose.ui)
110112
implementation(libs.bundles.compose.accompanist.core)
113+
// DI
114+
implementation(libs.bundles.dagger.all)
115+
kapt(libs.bundles.dagger.kapt)
116+
kapt(libs.hilt.compiler)
111117
}
112118

113119
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class).all {

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@
33
xmlns:tools="http://schemas.android.com/tools">
44

55
<application
6+
android:name=".ui.App"
67
android:allowBackup="true"
7-
android:dataExtractionRules="@xml/data_extraction_rules"
8-
android:fullBackupContent="@xml/backup_rules"
98
android:icon="@mipmap/ic_launcher"
109
android:label="@string/app_name"
1110
android:supportsRtl="true"
1211
android:theme="@style/Theme.ComposeMultipleBackstackDemo"
1312
android:enableOnBackInvokedCallback="true"
14-
tools:targetApi="31"
15-
>
13+
tools:targetApi="33">
14+
1615
<activity
1716
android:name=".ui.main.MainActivity"
1817
android:exported="true"
@@ -23,6 +22,7 @@
2322
<category android:name="android.intent.category.LAUNCHER" />
2423
</intent-filter>
2524
</activity>
25+
2626
</application>
2727

2828
</manifest>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.velord.composemultiplebackstackdemo.ui
2+
3+
import android.app.Application
4+
import dagger.hilt.android.HiltAndroidApp
5+
6+
@HiltAndroidApp
7+
class App : Application()
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.velord.composemultiplebackstackdemo.ui.di
2+
3+
import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.BottomNavEventService
4+
import dagger.Module
5+
import dagger.Provides
6+
import dagger.hilt.InstallIn
7+
import dagger.hilt.components.SingletonComponent
8+
import javax.inject.Singleton
9+
10+
@Module
11+
@InstallIn(SingletonComponent::class)
12+
object AppModule {
13+
14+
@Singleton
15+
@Provides
16+
fun provideBottomNavEventService(): BottomNavEventService = BottomNavEventService
17+
}

app/src/main/java/com/velord/composemultiplebackstackdemo/ui/main/MainActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import androidx.core.view.WindowCompat
88
import androidx.navigation.fragment.NavHostFragment
99
import com.velord.composemultiplebackstackdemo.R
1010
import com.velord.composemultiplebackstackdemo.databinding.ActivityMainBinding
11+
import dagger.hilt.android.AndroidEntryPoint
1112

13+
@AndroidEntryPoint
1214
class MainActivity : AppCompatActivity() {
1315

1416
companion object {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation
2+
3+
import kotlinx.coroutines.flow.MutableStateFlow
4+
5+
data class BottomNavBackHandlingState(
6+
val isAtStartGraphDestination: Boolean = true,
7+
val isGrantedToProceed: Boolean = false
8+
) {
9+
val isEnabled: Boolean get() = isAtStartGraphDestination && isGrantedToProceed
10+
}
11+
12+
object BottomNavEventService {
13+
val backHandlingStateFlow = MutableStateFlow(BottomNavBackHandlingState())
14+
15+
fun updateBackHandlingState(newState: BottomNavBackHandlingState) {
16+
backHandlingStateFlow.value = newState
17+
}
18+
}

app/src/main/java/com/velord/composemultiplebackstackdemo/ui/main/bottomNavigation/BottomNavFragment.kt

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation
22

3+
import android.content.Context
34
import android.os.Bundle
45
import android.util.Log
6+
import android.view.Gravity
57
import android.view.View
8+
import android.widget.Toast
9+
import androidx.activity.addCallback
610
import androidx.compose.foundation.layout.Column
711
import androidx.compose.foundation.layout.height
812
import androidx.compose.foundation.layout.navigationBarsPadding
@@ -38,10 +42,35 @@ import com.velord.composemultiplebackstackdemo.ui.navigation.BottomNavigationIte
3842
import com.velord.composemultiplebackstackdemo.ui.utils.viewLifecycleScope
3943
import com.velord.multiplebackstackapplier.MultipleBackstack
4044
import com.velord.multiplebackstackapplier.utils.compose.SnackBarOnBackPressHandler
45+
import dagger.hilt.android.AndroidEntryPoint
4146
import kotlinx.coroutines.launch
4247

43-
private const val TAG = "multiplebackstackapplierDEMO"
48+
private const val TAG = "BottomNav"
4449

50+
private fun Context.fireToast(text: String) {
51+
val description = "I am at the first at $text"
52+
Toast.makeText(this, description, Toast.LENGTH_SHORT).apply {
53+
setGravity(Gravity.CENTER_VERTICAL, 0, 0)
54+
show()
55+
}
56+
}
57+
58+
internal fun Fragment.addTestCallback(
59+
tag: String,
60+
viewModel: BottomNavViewModel
61+
) {
62+
requireActivity().onBackPressedDispatcher.addCallback(
63+
this,
64+
true
65+
) {
66+
requireContext().fireToast(tag)
67+
isEnabled = false
68+
viewModel.graphCompletedHandling()
69+
Log.d(TAG, "onBackPressedDispatcher")
70+
}
71+
}
72+
73+
@AndroidEntryPoint
4574
class BottomNavFragment : Fragment(R.layout.fragment_bottom_nav) {
4675

4776
private val navController by lazy {
@@ -105,24 +134,25 @@ class BottomNavFragment : Fragment(R.layout.fragment_bottom_nav) {
105134
@Composable
106135
private fun BottomNavScreen(viewModel: BottomNavViewModel) {
107136
val tabFlow = viewModel.currentTabFlow.collectAsStateWithLifecycle()
108-
val isBackHandlingEnabledState =
109-
viewModel.isBackHandlingEnabledFlow.collectAsStateWithLifecycle()
110-
Log.d(TAG, "isBackHandlingEnabledState: ${isBackHandlingEnabledState.value}")
137+
val backHandlingState = viewModel.backHandlingStateFlow.collectAsStateWithLifecycle()
138+
Log.d(TAG, "isBackHandlingEnabledState: ${backHandlingState.value}")
111139

112140
Content(
113141
selectedItem = tabFlow.value,
114142
onClick = viewModel::onTabClick,
115143
)
116144

117-
val str = stringResource(id = R.string.press_again_to_exit)
118-
SnackBarOnBackPressHandler(
119-
message = str,
120-
modifier = Modifier.padding(horizontal = 8.dp),
121-
enabled = isBackHandlingEnabledState.value,
122-
onBackClickLessThanDuration = viewModel::onBackDoubleClick,
123-
) {
124-
Snackbar {
125-
Text(text = it.visuals.message)
145+
if (backHandlingState.value.isEnabled) {
146+
val str = stringResource(id = R.string.press_again_to_exit)
147+
SnackBarOnBackPressHandler(
148+
message = str,
149+
modifier = Modifier.padding(horizontal = 8.dp),
150+
enabled = backHandlingState.value.isEnabled,
151+
onBackClickLessThanDuration = viewModel::onBackDoubleClick,
152+
) {
153+
Snackbar {
154+
Text(text = it.visuals.message)
155+
}
126156
}
127157
}
128158
}

app/src/main/java/com/velord/composemultiplebackstackdemo/ui/main/bottomNavigation/BottomNavViewModel.kt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,22 @@ import androidx.lifecycle.viewModelScope
55
import androidx.navigation.NavDestination
66
import com.velord.composemultiplebackstackdemo.ui.navigation.BottomNavigationItem
77
import com.velord.multiplebackstackapplier.utils.isCurrentStartDestination
8+
import dagger.hilt.android.lifecycle.HiltViewModel
89
import kotlinx.coroutines.flow.MutableSharedFlow
910
import kotlinx.coroutines.flow.MutableStateFlow
1011
import kotlinx.coroutines.launch
12+
import javax.inject.Inject
1113

12-
class BottomNavViewModel : ViewModel() {
14+
@HiltViewModel
15+
class BottomNavViewModel @Inject constructor(
16+
private val bottomNavEventService: BottomNavEventService
17+
): ViewModel() {
1318

1419
val currentTabFlow = MutableStateFlow(BottomNavigationItem.Left)
15-
val isBackHandlingEnabledFlow = MutableStateFlow(false)
20+
val backHandlingStateFlow = bottomNavEventService.backHandlingStateFlow
1621
val finishAppEvent: MutableSharedFlow<Unit> = MutableSharedFlow()
1722

18-
fun getNavigationItems() = BottomNavigationItem.values().toList()
23+
fun getNavigationItems() = BottomNavigationItem.entries
1924

2025
fun onTabClick(newTab: BottomNavigationItem) {
2126
if (newTab == currentTabFlow.value) return
@@ -28,6 +33,20 @@ class BottomNavViewModel : ViewModel() {
2833

2934
fun updateBackHandling(currentNavigationDestination: NavDestination?) {
3035
val isStart = currentNavigationDestination.isCurrentStartDestination(getNavigationItems())
31-
isBackHandlingEnabledFlow.value = isStart
36+
val newState = backHandlingStateFlow.value.copy(isAtStartGraphDestination = isStart)
37+
bottomNavEventService.updateBackHandlingState(newState)
38+
}
39+
40+
private fun changeGrantedToProceed(isGranted: Boolean) {
41+
val newState = backHandlingStateFlow.value.copy(isGrantedToProceed = isGranted)
42+
bottomNavEventService.updateBackHandlingState(newState)
43+
}
44+
45+
fun graphCompletedHandling() {
46+
changeGrantedToProceed(true)
47+
}
48+
49+
fun graphTakeResponsibility() {
50+
changeGrantedToProceed(false)
3251
}
3352
}

app/src/main/java/com/velord/composemultiplebackstackdemo/ui/main/bottomNavigation/center/CenterGraphFragment.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,22 @@ import android.view.LayoutInflater
55
import android.view.View
66
import android.view.ViewGroup
77
import androidx.fragment.app.Fragment
8+
import androidx.fragment.app.viewModels
89
import androidx.navigation.findNavController
910
import com.velord.composemultiplebackstackdemo.R
1011
import com.velord.composemultiplebackstackdemo.ui.compose.screen.AddNewScreen
1112
import com.velord.composemultiplebackstackdemo.ui.compose.theme.setContentWithTheme
13+
import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.BottomNavViewModel
14+
import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.addTestCallback
15+
import dagger.hilt.android.AndroidEntryPoint
1216

17+
private const val TAG = "center"
18+
19+
@AndroidEntryPoint
1320
class CenterGraphFragment : Fragment() {
1421

22+
private val viewModel by viewModels<BottomNavViewModel>()
23+
1524
override fun onCreateView(
1625
inflater: LayoutInflater,
1726
container: ViewGroup?,
@@ -21,4 +30,9 @@ class CenterGraphFragment : Fragment() {
2130
findNavController().navigate(R.id.toInDevelopmentFragmentFromCenterGraphFragment)
2231
}
2332
}
33+
34+
override fun onCreate(savedInstanceState: Bundle?) {
35+
super.onCreate(savedInstanceState)
36+
addTestCallback(TAG, viewModel)
37+
}
2438
}

app/src/main/java/com/velord/composemultiplebackstackdemo/ui/main/bottomNavigation/left/LeftGraphFragment.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,22 @@ import android.view.LayoutInflater
55
import android.view.View
66
import android.view.ViewGroup
77
import androidx.fragment.app.Fragment
8+
import androidx.fragment.app.viewModels
89
import androidx.navigation.findNavController
910
import com.velord.composemultiplebackstackdemo.R
1011
import com.velord.composemultiplebackstackdemo.ui.compose.screen.AddNewScreen
1112
import com.velord.composemultiplebackstackdemo.ui.compose.theme.setContentWithTheme
13+
import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.BottomNavViewModel
14+
import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.addTestCallback
15+
import dagger.hilt.android.AndroidEntryPoint
1216

17+
private const val TAG = "left"
18+
19+
@AndroidEntryPoint
1320
class LeftGraphFragment : Fragment() {
1421

22+
private val viewModel by viewModels<BottomNavViewModel>()
23+
1524
override fun onCreateView(
1625
inflater: LayoutInflater,
1726
container: ViewGroup?,
@@ -21,4 +30,9 @@ class LeftGraphFragment : Fragment() {
2130
findNavController().navigate(R.id.toInDevelopmentFragmentFromLeftGraphFragment)
2231
}
2332
}
33+
34+
override fun onCreate(savedInstanceState: Bundle?) {
35+
super.onCreate(savedInstanceState)
36+
addTestCallback(TAG, viewModel)
37+
}
2438
}

0 commit comments

Comments
 (0)