Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Don't Merge] This branch demonstrates use of basic xml views and compose views with multi module project and clean architecture #39

Open
wants to merge 1 commit into
base: baseproject
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ dependencies {

Lib.Androidx.list.forEach(::api)
Lib.Androidx.Compose.list.forEach(::api)
Lib.Androidx.Navigation.list.forEach(::api)

Lib.ThirdParty.list.forEach(::api)
Lib.Accompanist.list.forEach(::api)
Lib.Google.list.forEach(::api)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mutualmobile.praxis.di

import com.mutualmobile.praxis.navigator.ComposeNavigator
import com.mutualmobile.praxis.navigator.composenavigator.PraxisCloneComposeNavigator
import com.mutualmobile.praxis.navigator.AbsComposeNavigator
import com.mutualmobile.praxis.navigator.navigators.PraxisComposeNavigator
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -14,5 +14,5 @@ abstract class NavigationModule {

@Binds
@Singleton
abstract fun provideComposeNavigator(praxisComposeNavigator: PraxisCloneComposeNavigator): ComposeNavigator
abstract fun provideComposeNavigator(praxisComposeNavigator: PraxisComposeNavigator): AbsComposeNavigator
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.insets.ProvideWindowInsets
import dagger.hilt.android.AndroidEntryPoint
import com.mutualmobile.praxis.navigator.ComposeNavigator
import com.mutualmobile.praxis.navigator.AbsComposeNavigator
import com.mutualmobile.praxis.navigator.PraxisRoute
import com.mutualmobile.praxis.uionboarding.nav.onboardingNavigation
import com.praxis.feat.authentication.nav.authNavGraph
Expand All @@ -20,7 +20,7 @@ import javax.inject.Inject
class OnboardingActivity : AppCompatActivity() {

@Inject
lateinit var composeNavigator: ComposeNavigator
lateinit var absComposeNavigator: AbsComposeNavigator

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -31,7 +31,7 @@ class OnboardingActivity : AppCompatActivity() {
val navController = rememberNavController()

LaunchedEffect(Unit) {
composeNavigator.handleNavigationCommands(navController)
absComposeNavigator.handleNavigationCommands(navController)
}

ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Expand All @@ -40,7 +40,7 @@ class OnboardingActivity : AppCompatActivity() {
startDestination = PraxisRoute.OnBoarding.name,
) {
onboardingNavigation(
composeNavigator = composeNavigator,
absComposeNavigator = absComposeNavigator,
)
authNavGraph()
}
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/res/layout/main.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/app_nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
31 changes: 0 additions & 31 deletions app/src/main/res/layout/view_random_photos.xml

This file was deleted.

18 changes: 16 additions & 2 deletions buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ object Lib {
SPLASH_SCREEN_API
)

object Navigation {
private const val nav_version = "2.4.1"

// Kotlin
private const val NAV_FRAGMENT = "androidx.navigation:navigation-fragment-ktx:$nav_version"
private const val NAV_UI_KTX = "androidx.navigation:navigation-ui-ktx:$nav_version"

// Feature module Support
private const val NAV_DYNAMIC_MODULES =
"androidx.navigation:navigation-dynamic-features-fragment:$nav_version"
private const val COMPOSE_NAVIGATION = "androidx.navigation:navigation-compose:2.5.0-alpha01"

val list = listOf(NAV_FRAGMENT, NAV_UI_KTX, NAV_DYNAMIC_MODULES, COMPOSE_NAVIGATION)

}

object Compose {
private const val ACTIVITY_COMPOSE = "androidx.activity:activity-compose:${composeVersion}"
private const val CONSTRAINT_LAYOUT_COMPOSE =
Expand All @@ -66,7 +82,6 @@ object Lib {
private const val COMPOSE_MATERIAL = "androidx.compose.material:material:${composeVersion}"
private const val COMPOSE_TOOLING = "androidx.compose.ui:ui-tooling-preview:${composeVersion}"
private const val COMPOSE_DEBUG_TOOLING = "androidx.compose.ui:ui-tooling:${composeVersion}"
private const val COMPOSE_NAVIGATION = "androidx.navigation:navigation-compose:2.5.0-alpha01"

val list = listOf(
CONSTRAINT_LAYOUT_COMPOSE,
Expand All @@ -76,7 +91,6 @@ object Lib {
COMPOSE_MATERIAL,
COMPOSE_TOOLING,
COMPOSE_DEBUG_TOOLING,
COMPOSE_NAVIGATION,
)
}
}
Expand Down
1 change: 1 addition & 0 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies {

Lib.Androidx.list.forEach(::implementation)
Lib.Androidx.Compose.list.forEach(::implementation)
Lib.Androidx.Navigation.list.forEach(::implementation)
Lib.ThirdParty.list.forEach(::implementation)
Lib.Accompanist.list.forEach(::implementation)
Lib.Google.list.forEach(::implementation)
Expand Down
1 change: 1 addition & 0 deletions commonui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ dependencies {

Lib.Androidx.list.forEach(::implementation)
Lib.Androidx.Compose.list.forEach(::implementation)
Lib.Androidx.Navigation.list.forEach(::implementation)
Lib.ThirdParty.list.forEach(::implementation)
Lib.Accompanist.list.forEach(::implementation)
Lib.Google.list.forEach(::implementation)
Expand Down
6 changes: 6 additions & 0 deletions commonui/src/main/res/navigation/app_nav_graph.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/app_nav_graph">

</navigation>
1 change: 1 addition & 0 deletions navigator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ dependencies {

Lib.Androidx.list.forEach(::implementation)
Lib.Androidx.Compose.list.forEach(::implementation)
Lib.Androidx.Navigation.list.forEach(::implementation)
Lib.ThirdParty.list.forEach(::implementation)
Lib.Accompanist.list.forEach(::implementation)
Lib.Google.list.forEach(::implementation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,16 @@ sealed class ComposeNavigationCommand : NavigationCommand() {
) : ComposeNavigationCommand()

data class PopUpToRoute(val route: String, val inclusive: Boolean) : ComposeNavigationCommand()
}
}

sealed class FragmentNavigationCommand : NavigationCommand() {
data class NavigateToFragmentDestination(val destination: Int, val options: NavOptions? = null) : FragmentNavigationCommand()
data class NavigateUpWithResult<T>(
val key: String,
val result: T,
val destination: Int? = null
) : FragmentNavigationCommand()

data class PopUpToDestination(val destination: Int, val inclusive: Boolean) : FragmentNavigationCommand()

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ abstract class Navigator {

}

abstract class ComposeNavigator : Navigator() {
abstract class AbsComposeNavigator : Navigator() {
abstract fun navigate(route: String, optionsBuilder: (NavOptionsBuilder.() -> Unit)? = null)
abstract fun <T> observeResult(key: String, route: String? = null): Flow<T>
abstract fun <T> navigateBackWithResult(key: String, result: T, route: String?)
Expand All @@ -31,8 +31,8 @@ abstract class ComposeNavigator : Navigator() {

suspend fun handleNavigationCommands(navController: NavController) {
navigationCommands
.onSubscription { this@ComposeNavigator.navControllerFlow.value = navController }
.onCompletion { this@ComposeNavigator.navControllerFlow.value = null }
.onSubscription { this@AbsComposeNavigator.navControllerFlow.value = navController }
.onCompletion { this@AbsComposeNavigator.navControllerFlow.value = null }
.collect { navController.handleComposeNavigationCommand(it) }
}

Expand Down Expand Up @@ -72,8 +72,66 @@ abstract class ComposeNavigator : Navigator() {
}
}

abstract class AbsFragmentNavigatior : Navigator() {
abstract fun navigateFragment(
destination: Int,
optionsBuilder: (NavOptionsBuilder.() -> Unit)? = null
)

abstract fun <T> observeResult(key: String, destination: Int? = null): Flow<T>
abstract fun <T> navigateBackWithResult(key: String, result: T, destination: Int?)

abstract fun popUpTo(destination: Int, inclusive: Boolean)
abstract fun navigateAndClearBackStack(destination: Int)

suspend fun handleNavigationCommands(navController: NavController) {
navigationCommands
.onSubscription { [email protected] = navController }
.onCompletion { [email protected] = null }
.collect { navController.handleFragmentNavigationCommand(it) }
}

private fun NavController.handleFragmentNavigationCommand(navigationCommand: NavigationCommand) {
when (navigationCommand) {
NavigationCommand.NavigateUp -> navigateUp()
is FragmentNavigationCommand.NavigateUpWithResult<*> -> {
navUpWithResult(navigationCommand)
}
is FragmentNavigationCommand.NavigateToFragmentDestination -> navigate(
navigationCommand.destination,
null,
navigationCommand.options
)
is FragmentNavigationCommand.PopUpToDestination -> {
popBackStack(
navigationCommand.destination,
navigationCommand.inclusive
)
}
else -> {
throw RuntimeException("can't handle this with FragmentNavGraphNavigator")
}
}
}

private fun NavController.navUpWithResult(navigationCommand: FragmentNavigationCommand.NavigateUpWithResult<*>) {
val backStackEntry =
navigationCommand.destination?.let { getBackStackEntry(it) }
?: previousBackStackEntry
backStackEntry?.savedStateHandle?.set(
navigationCommand.key,
navigationCommand.result
)

navigationCommand.destination?.let {
popBackStack(it, false)
} ?: run {
navigateUp()
}
}
}


@OptIn(DelicateCoroutinesApi::class)
fun <T> LiveData<T>.asFlow(): Flow<T> = flow {
val channel = Channel<T>(Channel.CONFLATED)
val observer = Observer<T> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package com.mutualmobile.praxis.navigator.composenavigator
package com.mutualmobile.praxis.navigator.navigators

import androidx.navigation.NavOptionsBuilder
import androidx.navigation.navOptions
import com.mutualmobile.praxis.navigator.ComposeNavigationCommand
import com.mutualmobile.praxis.navigator.ComposeNavigator
import com.mutualmobile.praxis.navigator.AbsComposeNavigator
import com.mutualmobile.praxis.navigator.asFlow
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import javax.inject.Inject

class PraxisCloneComposeNavigator @Inject constructor(): ComposeNavigator() {
class PraxisComposeNavigator @Inject constructor(): AbsComposeNavigator() {

override fun navigate(route: String, optionsBuilder: (NavOptionsBuilder.() -> Unit)?) {
val options = optionsBuilder?.let { navOptions(it) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.mutualmobile.praxis.navigator.navigators

import androidx.navigation.NavOptionsBuilder
import androidx.navigation.navOptions
import com.mutualmobile.praxis.navigator.AbsFragmentNavigatior
import com.mutualmobile.praxis.navigator.FragmentNavigationCommand
import com.mutualmobile.praxis.navigator.asFlow
import kotlinx.coroutines.flow.*
import javax.inject.Inject

class PraxisFragmentNavigator @Inject constructor() : AbsFragmentNavigatior() {

override fun navigateFragment(destination: Int, optionsBuilder: (NavOptionsBuilder.() -> Unit)?) {
val options = optionsBuilder?.let { navOptions(it) }
navigationCommands.tryEmit(
FragmentNavigationCommand.NavigateToFragmentDestination(
destination,
options
)
)
}

override fun navigateAndClearBackStack(destination: Int) {
navigationCommands.tryEmit(
FragmentNavigationCommand.NavigateToFragmentDestination(
destination,
navOptions {
popUpTo(0)
})
)
}

override fun popUpTo(destination: Int, inclusive: Boolean) {
navigationCommands.tryEmit(FragmentNavigationCommand.PopUpToDestination(destination, inclusive))
}

override fun <T> navigateBackWithResult(key: String, result: T, destination: Int?) {
navigationCommands.tryEmit(
FragmentNavigationCommand.NavigateUpWithResult(
key = key,
result = result,
destination = destination
)
)
}

override fun <T> observeResult(key: String, destination: Int?): Flow<T> {
return navControllerFlow
.filterNotNull()
.flatMapLatest { navController ->
val backStackEntry = destination?.let { navController.getBackStackEntry(it) }
?: navController.currentBackStackEntry

backStackEntry?.savedStateHandle?.let { savedStateHandle ->
savedStateHandle.getLiveData<T?>(key)
.asFlow()
.filter { it != null }
.onEach {
// Nullify the result to avoid resubmitting it
savedStateHandle.set(key, null)
}
} ?: emptyFlow()
}
}
}
1 change: 1 addition & 0 deletions ui-authentication/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ dependencies {

Lib.Androidx.list.forEach(::implementation)
Lib.Androidx.Compose.list.forEach(::implementation)
Lib.Androidx.Navigation.list.forEach(::implementation)
Lib.ThirdParty.list.forEach(::implementation)
Lib.Accompanist.list.forEach(::implementation)
Lib.Google.list.forEach(::implementation)
Expand Down
Loading