From 0e5bab9ee34eb4e5c44ce3303f86e0d4076e481d Mon Sep 17 00:00:00 2001 From: AmadeyKuspakov Date: Mon, 10 Jul 2023 01:54:31 +0400 Subject: [PATCH] Major refactoring of architecture #5 --- oauth/build.gradle.kts | 2 +- .../co/soramitsu/oauth/base/CardActivity.kt | 23 +- .../soramitsu/oauth/base/SoraCardNavGraph.kt | 196 ++++++++---------- .../oauth/base/navigation/NavigationExt.kt | 16 +- .../flow/login/impl/LoginFlowImpl.kt | 2 +- .../registration/impl/RegistrationFlowImpl.kt | 2 +- .../api/VerificationDestination.kt | 2 +- .../verification/impl/VerificationFlowImpl.kt | 6 +- .../oauth/core/engines/EnginesBinderModule.kt | 12 +- .../core/engines/EnginesProviderModule.kt | 14 +- .../core/engines/router/api/ComposeRouter.kt | 5 +- .../engines/router/impl/ComposeRouterImpl.kt | 17 +- 12 files changed, 131 insertions(+), 166 deletions(-) diff --git a/oauth/build.gradle.kts b/oauth/build.gradle.kts index 856ea44b..3a5ee7a9 100644 --- a/oauth/build.gradle.kts +++ b/oauth/build.gradle.kts @@ -110,7 +110,7 @@ dependencies { implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1") implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1") - implementation("androidx.navigation:navigation-compose:2.5.3") + implementation("androidx.navigation:navigation-compose:2.6.0") implementation("com.google.accompanist:accompanist-navigation-animation:0.30.1") implementation( "com.google.dagger:hilt-android:$hiltVersion") diff --git a/oauth/src/main/java/jp/co/soramitsu/oauth/base/CardActivity.kt b/oauth/src/main/java/jp/co/soramitsu/oauth/base/CardActivity.kt index 3592e7bc..3e98b165 100644 --- a/oauth/src/main/java/jp/co/soramitsu/oauth/base/CardActivity.kt +++ b/oauth/src/main/java/jp/co/soramitsu/oauth/base/CardActivity.kt @@ -9,8 +9,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.CircularProgressIndicator -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -33,11 +31,10 @@ import jp.co.soramitsu.oauth.base.sdk.contract.SoraCardContractData import jp.co.soramitsu.oauth.base.sdk.toPayWingsType import jp.co.soramitsu.oauth.common.navigation.coordinator.api.NavigationCoordinator import jp.co.soramitsu.oauth.core.engines.activityresult.api.ActivityResult +import jp.co.soramitsu.oauth.core.engines.activityresult.api.SoraCardResult import jp.co.soramitsu.oauth.core.engines.router.api.ComposeRouter import jp.co.soramitsu.oauth.core.engines.router.api.SoraCardDestinations import jp.co.soramitsu.oauth.theme.AuthSdkTheme -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject @@ -63,16 +60,8 @@ class CardActivity : AppCompatActivity(R.layout.card_activity) { super.onCreate(savedInstanceState) setContent { AuthSdkTheme { - val startDestination = composeRouter.startDestination.collectAsState() - - LaunchedEffect(Unit) { - composeRouter.startDestination.onEach { - println("This is checkpoint: composeRouter.startDestination - $it") - }.launchIn(this) - } - Box(modifier = Modifier.fillMaxSize()) { - val isLoading = remember(startDestination.value) { + val isLoading = remember(composeRouter.startDestination.value) { derivedStateOf { composeRouter.startDestination.value === SoraCardDestinations.Loading } @@ -88,7 +77,7 @@ class CardActivity : AppCompatActivity(R.layout.card_activity) { SoraCardNavGraph( navHostController = composeRouter.navController, - startDestination = startDestination.value, + startDestination = composeRouter.startDestination.value, ) } } @@ -166,4 +155,10 @@ class CardActivity : AppCompatActivity(R.layout.card_activity) { ) } } + + override fun onBackPressed() { + super.onBackPressed() + activityResult.setResult(SoraCardResult.Canceled) + finish() + } } diff --git a/oauth/src/main/java/jp/co/soramitsu/oauth/base/SoraCardNavGraph.kt b/oauth/src/main/java/jp/co/soramitsu/oauth/base/SoraCardNavGraph.kt index bcb24ff7..057a356e 100644 --- a/oauth/src/main/java/jp/co/soramitsu/oauth/base/SoraCardNavGraph.kt +++ b/oauth/src/main/java/jp/co/soramitsu/oauth/base/SoraCardNavGraph.kt @@ -1,28 +1,22 @@ package jp.co.soramitsu.oauth.base import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable -import androidx.compose.runtime.movableContentOf import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.NavHostController -import androidx.navigation.NavType import androidx.navigation.compose.dialog -import androidx.navigation.navArgument import com.google.accompanist.navigation.animation.AnimatedNavHost import com.google.accompanist.navigation.animation.rememberAnimatedNavController -import jp.co.soramitsu.oauth.base.navigation.Argument -import jp.co.soramitsu.oauth.base.navigation.Destination import jp.co.soramitsu.oauth.base.navigation.animatedComposable -import jp.co.soramitsu.oauth.base.navigation.path -import jp.co.soramitsu.oauth.base.navigation.requireArguments -import jp.co.soramitsu.oauth.base.navigation.requireString import jp.co.soramitsu.oauth.common.navigation.flow.login.api.LoginDestination import jp.co.soramitsu.oauth.common.navigation.flow.registration.api.RegistrationDestination import jp.co.soramitsu.oauth.common.navigation.flow.verification.api.VerificationDestination import jp.co.soramitsu.oauth.core.engines.router.api.SoraCardDestinations import jp.co.soramitsu.oauth.feature.login.terms.TermsAndConditionsScreen -import jp.co.soramitsu.oauth.feature.login.web.WebPageScreen import jp.co.soramitsu.oauth.feature.login.enterphone.EnterPhoneNumberScreen import jp.co.soramitsu.oauth.feature.login.enterotp.VerifyPhoneNumberScreen import jp.co.soramitsu.oauth.feature.registration.enternames.RegisterUserScreen @@ -42,63 +36,56 @@ internal fun SoraCardNavGraph( navHostController: NavHostController, startDestination: SoraCardDestinations, ) { - println("This is checkpoint: SoraCardNavGraph recompose - ${startDestination.route}") - val isGraphVisible = remember(startDestination) { startDestination !== SoraCardDestinations.Loading } - val navGraph = remember(startDestination) { - println("This is checkpoint: navGraph remember recompose - ${startDestination.route}") + if (isGraphVisible) { + AnimatedNavHost( + navController = navHostController, + startDestination = startDestination.route + ) { + animatedComposable( + route = LoginDestination.TermsAndConditions.route + ) { + TermsAndConditionsScreen() + } + + animatedComposable( + route = LoginDestination.EnterPhone.route + ) { + EnterPhoneNumberScreen() + } + + animatedComposable( + route = LoginDestination.EnterOtp.route + ) { + VerifyPhoneNumberScreen() + } + + animatedComposable( + route = RegistrationDestination.EnterFirstAndLastName.route + ) { + RegisterUserScreen() + } + + animatedComposable( + route = RegistrationDestination.EnterEmail.route, + ) { + EnterEmailScreen() + } - movableContentOf { - println("This is checkpoint: navGraph movableContentOf recompose - ${startDestination.route}") + animatedComposable( + route = RegistrationDestination.EmailConfirmation.route + ) { + VerifyEmailScreen() + } - AnimatedNavHost( - navController = navHostController, - startDestination = SoraCardDestinations.Loading.route + animatedComposable( + route = VerificationDestination.GetPrepared.route ) { - animatedComposable( - route = LoginDestination.TermsAndConditions.route - ) { - TermsAndConditionsScreen() - } - - animatedComposable( - route = LoginDestination.EnterPhone.route - ) { - EnterPhoneNumberScreen() - } - - animatedComposable( - route = LoginDestination.EnterOtp.route - ) { - VerifyPhoneNumberScreen() - } - - animatedComposable( - route = RegistrationDestination.EnterFirstAndLastName.route - ) { - RegisterUserScreen() - } - - animatedComposable( - route = RegistrationDestination.EnterEmail.route, - ) { - EnterEmailScreen() - } - - animatedComposable( - route = RegistrationDestination.EmailConfirmation.route - ) { - VerifyEmailScreen() - } - - animatedComposable( - route = VerificationDestination.GetPrepared.route - ) { - GetPreparedScreen() - } + GetPreparedScreen() + } // animatedComposable( // route = Destination.WEB_PAGE.route @@ -113,66 +100,53 @@ internal fun SoraCardNavGraph( // ) // } - animatedComposable( - route = VerificationDestination.VerificationFailed.route - ) { - VerificationFailedScreen() - } - - animatedComposable( - route = VerificationDestination.VerificationRejected.route - ) { - VerificationRejectedScreen() - } - - animatedComposable( - route = VerificationDestination.VerificationInProgress.route - ) { - VerificationInProgressScreen() - } - - animatedComposable( - route = VerificationDestination.VerificationSuccessful.route - ) { - VerificationSuccessfulScreen() - } - - animatedComposable( - route = VerificationDestination.NotEnoughXor.route - ) { - CardIssuanceScreen() - } - - dialog( - route = VerificationDestination.GetMoreXor.route - ) { - ChooseXorPurchaseMethodDialog() - } + animatedComposable( + route = VerificationDestination.VerificationFailed.route + ) { + VerificationFailedScreen() } + animatedComposable( + route = VerificationDestination.VerificationRejected.route + ) { + VerificationRejectedScreen() + } + + animatedComposable( + route = VerificationDestination.VerificationInProgress.route + ) { + VerificationInProgressScreen() + } + + animatedComposable( + route = VerificationDestination.VerificationSuccessful.route + ) { + VerificationSuccessfulScreen() + } + + animatedComposable( + route = VerificationDestination.NotEnoughXor.route + ) { + CardIssuanceScreen() + } - println("This is checkpoint: navHostController.graph - ${navHostController.graph.nodes}") + dialog( + route = VerificationDestination.GetMoreXor.route + ) { + ChooseXorPurchaseMethodDialog() + } } } - - navGraph.invoke() } @Preview @Composable @OptIn(ExperimentalAnimationApi::class) private fun PreviewSoraCardNavGraph() { - SoraCardNavGraph( - navHostController = rememberAnimatedNavController(), - startDestination = LoginDestination.TermsAndConditions - ) -} - - - - -// -// animatedComposable( -// route = SoraCardDestinations) { -// ChangeEmailScreen() -// } \ No newline at end of file + Box(modifier = Modifier.fillMaxSize()) { + SoraCardNavGraph( + navHostController = rememberAnimatedNavController(), + startDestination = LoginDestination.TermsAndConditions + ) + } +} \ No newline at end of file diff --git a/oauth/src/main/java/jp/co/soramitsu/oauth/base/navigation/NavigationExt.kt b/oauth/src/main/java/jp/co/soramitsu/oauth/base/navigation/NavigationExt.kt index 1b9d3eb1..44fdf79b 100644 --- a/oauth/src/main/java/jp/co/soramitsu/oauth/base/navigation/NavigationExt.kt +++ b/oauth/src/main/java/jp/co/soramitsu/oauth/base/navigation/NavigationExt.kt @@ -3,7 +3,11 @@ package jp.co.soramitsu.oauth.base.navigation import android.os.Bundle import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.core.CubicBezierEasing import androidx.compose.animation.core.tween +import androidx.compose.animation.expandVertically +import androidx.compose.animation.scaleIn +import androidx.compose.animation.shrinkVertically import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideOutHorizontally import androidx.compose.runtime.Composable @@ -27,24 +31,28 @@ fun NavGraphBuilder.animatedComposable( arguments = arguments, deepLinks = deepLinks, enterTransition = { + println("This is checkpoint: enterTransition - ${this.initialState.destination.route} to ${this.targetState.destination.route}") slideInHorizontally( initialOffsetX = { it }, animationSpec = tween(TRANSITION_DURATION) ) }, popEnterTransition = { + println("This is checkpoint: popEnterTransition - ${this.initialState.destination.route} to ${this.targetState.destination.route}") slideInHorizontally( initialOffsetX = { -it }, animationSpec = tween(TRANSITION_DURATION) ) }, exitTransition = { + println("This is checkpoint: exitTransition - ${this.initialState.destination.route} to ${this.targetState.destination.route}") slideOutHorizontally( targetOffsetX = { -it }, animationSpec = tween(TRANSITION_DURATION) ) }, popExitTransition = { + println("This is checkpoint: popExitTransition - ${this.initialState.destination.route} to ${this.targetState.destination.route}") slideOutHorizontally( targetOffsetX = { it }, animationSpec = tween(TRANSITION_DURATION) @@ -53,11 +61,3 @@ fun NavGraphBuilder.animatedComposable( content = content ) } - -fun NavBackStackEntry.requireArguments(): Bundle { - return arguments ?: throw IllegalStateException("Arguments is null") -} - -fun Bundle.requireString(key: String): String { - return getString(key) ?: throw IllegalStateException("Argument is null") -} diff --git a/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/login/impl/LoginFlowImpl.kt b/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/login/impl/LoginFlowImpl.kt index da1c9031..f73b81ae 100644 --- a/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/login/impl/LoginFlowImpl.kt +++ b/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/login/impl/LoginFlowImpl.kt @@ -35,7 +35,7 @@ class LoginFlowImpl @Inject constructor( } composeRouter.setNewStartDestination(destination) } - }.run { composeRouter.clearBackStack() } + } override fun onBack() { composeRouter.popBack() diff --git a/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/registration/impl/RegistrationFlowImpl.kt b/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/registration/impl/RegistrationFlowImpl.kt index a04c05cb..9910e472 100644 --- a/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/registration/impl/RegistrationFlowImpl.kt +++ b/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/registration/impl/RegistrationFlowImpl.kt @@ -38,7 +38,7 @@ class RegistrationFlowImpl @Inject constructor( } composeRouter.setNewStartDestination(destination) } - }.run { composeRouter.clearBackStack() } + } override fun onBack() { composeRouter.popBack() diff --git a/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/verification/api/VerificationDestination.kt b/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/verification/api/VerificationDestination.kt index 236c35b1..65fe9097 100644 --- a/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/verification/api/VerificationDestination.kt +++ b/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/verification/api/VerificationDestination.kt @@ -27,7 +27,7 @@ sealed interface VerificationDestination: SoraCardDestinations { override val route: String = "VERIFICATION_REJECTED" companion object: SoraCardDestinations { - override val route: String = "VERIFICATION_FAILED" + override val route: String = "VERIFICATION_REJECTED" const val ADDITIONAL_INFO_KEY = "ADDITIONAL_INFO_KEY" } diff --git a/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/verification/impl/VerificationFlowImpl.kt b/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/verification/impl/VerificationFlowImpl.kt index 94c65c27..236c81b7 100644 --- a/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/verification/impl/VerificationFlowImpl.kt +++ b/oauth/src/main/java/jp/co/soramitsu/oauth/common/navigation/flow/verification/impl/VerificationFlowImpl.kt @@ -48,7 +48,7 @@ class VerificationFlowImpl @Inject constructor( composeRouter.setNewStartDestination(destination) } else -> composeRouter.setNewStartDestination(destination) - }.run { composeRouter.clearBackStack() } + } override fun onBack() { composeRouter.popBack() @@ -81,9 +81,7 @@ class VerificationFlowImpl @Inject constructor( } override fun onTryAgain() { - if (inMemoryRepo.userAvailableXorAmount < 100) - composeRouter.setNewStartDestination(VerificationDestination.NotEnoughXor) else - composeRouter.setNewStartDestination(VerificationDestination.GetPrepared) + composeRouter.setNewStartDestination(VerificationDestination.GetPrepared) } override fun onOpenSupport() { diff --git a/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/EnginesBinderModule.kt b/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/EnginesBinderModule.kt index 29967c42..486d870d 100644 --- a/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/EnginesBinderModule.kt +++ b/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/EnginesBinderModule.kt @@ -27,6 +27,12 @@ interface ActivityRetainedEnginesBinderModule { fun bindActivityResult( activityResultImpl: ActivityResultImpl ): ActivityResult + + @Binds + @ActivityRetainedScoped + fun bindComposeRouter( + composeRouterImpl: ComposeRouterImpl + ): ComposeRouter } @Module @@ -51,10 +57,4 @@ interface SingletonEnginesBinderModule { restClientImpl: RestClientImpl ): RestClient - @Binds - @Singleton - fun bindComposeRouter( - composeRouterImpl: ComposeRouterImpl - ): ComposeRouter - } \ No newline at end of file diff --git a/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/EnginesProviderModule.kt b/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/EnginesProviderModule.kt index 30a97d2b..b5861c00 100644 --- a/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/EnginesProviderModule.kt +++ b/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/EnginesProviderModule.kt @@ -16,7 +16,9 @@ import com.google.accompanist.navigation.animation.AnimatedComposeNavigator import dagger.Module import dagger.Provides import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityRetainedComponent import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.scopes.ActivityRetainedScoped import dagger.hilt.components.SingletonComponent import io.ktor.client.HttpClient import io.ktor.client.engine.okhttp.OkHttp @@ -92,8 +94,15 @@ class EnginesProviderModule { } } +} + +@Module +@InstallIn(ActivityRetainedComponent::class) +@OptIn(ExperimentalAnimationApi::class) +class EnginesActivityRetainedProviderModule { + @Provides - @Singleton + @ActivityRetainedScoped fun provideNavHostController( @ApplicationContext context: Context ): NavHostController = @@ -104,8 +113,7 @@ class EnginesProviderModule { } @Provides - @Singleton + @ActivityRetainedScoped fun provideTimer() = Timer() - } \ No newline at end of file diff --git a/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/router/api/ComposeRouter.kt b/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/router/api/ComposeRouter.kt index 8ae2eced..12f21a0e 100644 --- a/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/router/api/ComposeRouter.kt +++ b/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/router/api/ComposeRouter.kt @@ -2,11 +2,10 @@ package jp.co.soramitsu.oauth.core.engines.router.api import androidx.compose.runtime.State import androidx.navigation.NavHostController -import kotlinx.coroutines.flow.StateFlow interface ComposeRouter { - val startDestination: StateFlow + val startDestination: State val navController: NavHostController @@ -15,6 +14,4 @@ interface ComposeRouter { fun navigateTo(destination: SoraCardDestinations) fun popBack() - - fun clearBackStack() } \ No newline at end of file diff --git a/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/router/impl/ComposeRouterImpl.kt b/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/router/impl/ComposeRouterImpl.kt index a9d16bf3..b2769328 100644 --- a/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/router/impl/ComposeRouterImpl.kt +++ b/oauth/src/main/java/jp/co/soramitsu/oauth/core/engines/router/impl/ComposeRouterImpl.kt @@ -1,38 +1,31 @@ package jp.co.soramitsu.oauth.core.engines.router.impl import androidx.compose.runtime.State -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf import androidx.navigation.NavHostController import jp.co.soramitsu.oauth.core.engines.router.api.ComposeRouter import jp.co.soramitsu.oauth.core.engines.router.api.SoraCardDestinations -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import javax.inject.Inject class ComposeRouterImpl @Inject constructor( private val navHostController: NavHostController ): ComposeRouter { - private val _startDestinationState = MutableStateFlow(SoraCardDestinations.Loading) + private val _startDestinationState = mutableStateOf(SoraCardDestinations.Loading) - override val startDestination: StateFlow = _startDestinationState + override val startDestination: State = _startDestinationState override val navController: NavHostController = navHostController override fun setNewStartDestination(destination: SoraCardDestinations) { -// _startDestinationState.value = destination + _startDestinationState.value = destination } override fun navigateTo(destination: SoraCardDestinations) { -// navHostController.navigate(destination.route) + navHostController.navigate(destination.route) } override fun popBack() { -// navHostController.popBackStack() - } - - override fun clearBackStack() { -// navHostController.popBackStack(navHostController.graph.id, true) + navHostController.popBackStack() } } \ No newline at end of file