Skip to content

Commit

Permalink
Merge pull request #5 from Velord/feature/BackHandling
Browse files Browse the repository at this point in the history
Feature/back handling
  • Loading branch information
Velord authored Dec 14, 2023
2 parents 2ee2f86 + e6db258 commit e6e3665
Show file tree
Hide file tree
Showing 19 changed files with 168 additions and 104 deletions.
8 changes: 7 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ plugins {
id(libs.plugins.android.application.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
id(libs.plugins.kotlin.kapt.get().pluginId)
id(libs.plugins.dagger.hilt.get().pluginId)
}

// When app incompatible with previous version change this value
val globalVersion = 0
// When you create huge feature(or many) release change this value
val majorVersion = 3
val majorVersion = 5
// When you create feature release change this value
val minorVersion = 0
// When you create fix change this value
Expand Down Expand Up @@ -104,10 +105,15 @@ dependencies {
// Templates
implementation(libs.bundles.kotlin.module)
implementation(libs.bundles.androidx.module)
implementation(libs.bundles.androidx.activity)
// Compose
implementation(libs.bundles.compose.material.third)
implementation(libs.bundles.compose.ui)
implementation(libs.bundles.compose.accompanist.core)
// DI
implementation(libs.bundles.dagger.all)
kapt(libs.bundles.dagger.kapt)
kapt(libs.hilt.compiler)
}

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class).all {
Expand Down
8 changes: 4 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@
xmlns:tools="http://schemas.android.com/tools">

<application
android:name=".ui.App"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.ComposeMultipleBackstackDemo"
android:enableOnBackInvokedCallback="true"
tools:targetApi="31"
>
tools:targetApi="33">

<activity
android:name=".ui.main.MainActivity"
android:exported="true"
Expand All @@ -23,6 +22,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.velord.composemultiplebackstackdemo.ui

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class App : Application()
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.velord.composemultiplebackstackdemo.ui.di

import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.BottomNavEventService
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

@Singleton
@Provides
fun provideBottomNavEventService(): BottomNavEventService = BottomNavEventService
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import androidx.core.view.WindowCompat
import androidx.navigation.fragment.NavHostFragment
import com.velord.composemultiplebackstackdemo.R
import com.velord.composemultiplebackstackdemo.databinding.ActivityMainBinding
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation

import kotlinx.coroutines.flow.MutableStateFlow

data class BottomNavBackHandlingState(
val isAtStartGraphDestination: Boolean = true,
val isGrantedToProceed: Boolean = false
) {
val isEnabled: Boolean get() = isAtStartGraphDestination && isGrantedToProceed
}

object BottomNavEventService {
val backHandlingStateFlow = MutableStateFlow(BottomNavBackHandlingState())

fun updateBackHandlingState(newState: BottomNavBackHandlingState) {
backHandlingStateFlow.value = newState
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation

import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.Gravity
import android.view.View
import android.widget.Toast
import androidx.activity.addCallback
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
Expand Down Expand Up @@ -38,10 +42,35 @@ import com.velord.composemultiplebackstackdemo.ui.navigation.BottomNavigationIte
import com.velord.composemultiplebackstackdemo.ui.utils.viewLifecycleScope
import com.velord.multiplebackstackapplier.MultipleBackstack
import com.velord.multiplebackstackapplier.utils.compose.SnackBarOnBackPressHandler
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch

private const val TAG = "multiplebackstackapplierDEMO"
private const val TAG = "BottomNav"

private fun Context.fireToast(text: String) {
val description = "I am at the first at $text"
Toast.makeText(this, description, Toast.LENGTH_SHORT).apply {
setGravity(Gravity.CENTER_VERTICAL, 0, 0)
show()
}
}

internal fun Fragment.addTestCallback(
tag: String,
viewModel: BottomNavViewModel
) {
requireActivity().onBackPressedDispatcher.addCallback(
this,
true
) {
requireContext().fireToast(tag)
isEnabled = false
viewModel.graphCompletedHandling()
Log.d(TAG, "onBackPressedDispatcher")
}
}

@AndroidEntryPoint
class BottomNavFragment : Fragment(R.layout.fragment_bottom_nav) {

private val navController by lazy {
Expand Down Expand Up @@ -105,24 +134,25 @@ class BottomNavFragment : Fragment(R.layout.fragment_bottom_nav) {
@Composable
private fun BottomNavScreen(viewModel: BottomNavViewModel) {
val tabFlow = viewModel.currentTabFlow.collectAsStateWithLifecycle()
val isBackHandlingEnabledState =
viewModel.isBackHandlingEnabledFlow.collectAsStateWithLifecycle()
Log.d(TAG, "isBackHandlingEnabledState: ${isBackHandlingEnabledState.value}")
val backHandlingState = viewModel.backHandlingStateFlow.collectAsStateWithLifecycle()
Log.d(TAG, "isBackHandlingEnabledState: ${backHandlingState.value}")

Content(
selectedItem = tabFlow.value,
onClick = viewModel::onTabClick,
)

val str = stringResource(id = R.string.press_again_to_exit)
SnackBarOnBackPressHandler(
message = str,
modifier = Modifier.padding(horizontal = 8.dp),
enabled = isBackHandlingEnabledState.value,
onBackClickLessThanDuration = viewModel::onBackDoubleClick,
) {
Snackbar {
Text(text = it.visuals.message)
if (backHandlingState.value.isEnabled) {
val str = stringResource(id = R.string.press_again_to_exit)
SnackBarOnBackPressHandler(
message = str,
modifier = Modifier.padding(horizontal = 8.dp),
enabled = backHandlingState.value.isEnabled,
onBackClickLessThanDuration = viewModel::onBackDoubleClick,
) {
Snackbar {
Text(text = it.visuals.message)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@ import androidx.lifecycle.viewModelScope
import androidx.navigation.NavDestination
import com.velord.composemultiplebackstackdemo.ui.navigation.BottomNavigationItem
import com.velord.multiplebackstackapplier.utils.isCurrentStartDestination
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

class BottomNavViewModel : ViewModel() {
@HiltViewModel
class BottomNavViewModel @Inject constructor(
private val bottomNavEventService: BottomNavEventService
): ViewModel() {

val currentTabFlow = MutableStateFlow(BottomNavigationItem.Left)
val isBackHandlingEnabledFlow = MutableStateFlow(false)
val backHandlingStateFlow = bottomNavEventService.backHandlingStateFlow
val finishAppEvent: MutableSharedFlow<Unit> = MutableSharedFlow()

fun getNavigationItems() = BottomNavigationItem.values().toList()
fun getNavigationItems() = BottomNavigationItem.entries

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

fun updateBackHandling(currentNavigationDestination: NavDestination?) {
val isStart = currentNavigationDestination.isCurrentStartDestination(getNavigationItems())
isBackHandlingEnabledFlow.value = isStart
val newState = backHandlingStateFlow.value.copy(isAtStartGraphDestination = isStart)
bottomNavEventService.updateBackHandlingState(newState)
}

private fun changeGrantedToProceed(isGranted: Boolean) {
val newState = backHandlingStateFlow.value.copy(isGrantedToProceed = isGranted)
bottomNavEventService.updateBackHandlingState(newState)
}

fun graphCompletedHandling() {
changeGrantedToProceed(true)
}

fun graphTakeResponsibility() {
changeGrantedToProceed(false)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.findNavController
import com.velord.composemultiplebackstackdemo.R
import com.velord.composemultiplebackstackdemo.ui.compose.screen.AddNewScreen
import com.velord.composemultiplebackstackdemo.ui.compose.theme.setContentWithTheme
import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.BottomNavViewModel
import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.addTestCallback
import dagger.hilt.android.AndroidEntryPoint

private const val TAG = "center"

@AndroidEntryPoint
class CenterGraphFragment : Fragment() {

private val viewModel by viewModels<BottomNavViewModel>()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
Expand All @@ -21,4 +30,9 @@ class CenterGraphFragment : Fragment() {
findNavController().navigate(R.id.toInDevelopmentFragmentFromCenterGraphFragment)
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addTestCallback(TAG, viewModel)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.findNavController
import com.velord.composemultiplebackstackdemo.R
import com.velord.composemultiplebackstackdemo.ui.compose.screen.AddNewScreen
import com.velord.composemultiplebackstackdemo.ui.compose.theme.setContentWithTheme
import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.BottomNavViewModel
import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.addTestCallback
import dagger.hilt.android.AndroidEntryPoint

private const val TAG = "left"

@AndroidEntryPoint
class LeftGraphFragment : Fragment() {

private val viewModel by viewModels<BottomNavViewModel>()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
Expand All @@ -21,4 +30,9 @@ class LeftGraphFragment : Fragment() {
findNavController().navigate(R.id.toInDevelopmentFragmentFromLeftGraphFragment)
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addTestCallback(TAG, viewModel)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,30 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.findNavController
import com.velord.composemultiplebackstackdemo.R
import com.velord.composemultiplebackstackdemo.ui.compose.screen.AddNewScreen
import com.velord.composemultiplebackstackdemo.ui.compose.theme.setContentWithTheme
import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.BottomNavViewModel
import com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.addTestCallback
import dagger.hilt.android.AndroidEntryPoint

private const val TAG = "right"

@AndroidEntryPoint
class RightGraphFragment : Fragment() {

private val viewModel by viewModels<BottomNavViewModel>()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = setContentWithTheme {
viewModel.graphTakeResponsibility()
addTestCallback(TAG, viewModel)

AddNewScreen(R.string.add_new_screen_right) {
findNavController().navigate(R.id.toInDevelopmentFragmentFromRightGraphFragment)
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/navigation/main_nav_graph.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
android:name="com.velord.composemultiplebackstackdemo.ui.main.bottomNavigation.BottomNavFragment"
android:label="BottomNavFragment"/>


<fragment
android:id="@+id/inDevelopmentFragment"
android:name="com.velord.composemultiplebackstackdemo.ui.main.overlay.OverlayFragment"
Expand Down
13 changes: 0 additions & 13 deletions app/src/main/res/xml/backup_rules.xml

This file was deleted.

19 changes: 0 additions & 19 deletions app/src/main/res/xml/data_extraction_rules.xml

This file was deleted.

Loading

0 comments on commit e6e3665

Please sign in to comment.