From be8ed7a8ad87db058814347f3686bb30198508ae Mon Sep 17 00:00:00 2001 From: Milan Cerovsky Date: Mon, 19 Aug 2024 16:12:15 +0200 Subject: [PATCH] Activity Compose Composition Strategy handling --- .../co/electriccoin/zcash/ui/MainActivity.kt | 13 +++- .../common/extension/ComponentActivityExt.kt | 67 +++++++++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 ui-lib/src/main/java/co/electriccoin/zcash/ui/common/extension/ComponentActivityExt.kt diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt index 87715e9f..d69ec296 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.content.pm.ActivityInfo import android.os.Bundle import android.os.SystemClock -import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels import androidx.annotation.VisibleForTesting @@ -29,6 +28,7 @@ import cash.z.ecc.sdk.type.fromResources import co.electriccoin.zcash.spackle.FirebaseTestLabUtil import co.electriccoin.zcash.spackle.Twig import co.electriccoin.zcash.ui.common.compose.BindCompLocalProvider +import co.electriccoin.zcash.ui.common.extension.setContentCompat import co.electriccoin.zcash.ui.common.model.OnboardingState import co.electriccoin.zcash.ui.common.model.WalletRestoringState import co.electriccoin.zcash.ui.common.viewmodel.AuthenticationUIState @@ -126,8 +126,7 @@ class MainActivity : FragmentActivity() { // including IME animations, and go edge-to-edge. // This also sets up the initial system bar style based on the platform theme enableEdgeToEdge() - - setContent { + setContentCompat { Override(configurationOverrideFlow) { ZcashTheme { BlankSurface( @@ -160,6 +159,7 @@ class MainActivity : FragmentActivity() { Twig.debug { "Authentication initial state" } // Wait for the state update } + AuthenticationUIState.NotRequired -> { Twig.debug { "App access authentication NOT required - welcome animation only" } if (animateAppAccess) { @@ -173,6 +173,7 @@ class MainActivity : FragmentActivity() { } } } + AuthenticationUIState.Required -> { Twig.debug { "App access authentication required" } @@ -198,12 +199,14 @@ class MainActivity : FragmentActivity() { useCase = AuthenticationUseCase.AppAccess ) } + AuthenticationUIState.SupportedRequired -> { Twig.debug { "Authentication support required" } WrapSupport( goBack = { finish() } ) } + AuthenticationUIState.Successful -> { Twig.debug { "Authentication successful - entering the app" } // No action is needed - the main app content is laid out now @@ -229,6 +232,7 @@ class MainActivity : FragmentActivity() { SecretState.None -> { WrapOnboarding() } + is SecretState.NeedsWarning -> { WrapSecurityWarning( onBack = { walletViewModel.persistOnboardingState(OnboardingState.NONE) }, @@ -249,15 +253,18 @@ class MainActivity : FragmentActivity() { } ) } + is SecretState.NeedsBackup -> { WrapNewWalletRecovery( secretState.persistableWallet, onBackupComplete = { walletViewModel.persistOnboardingState(OnboardingState.READY) } ) } + is SecretState.Ready -> { Navigation() } + else -> { error("Unhandled secret state: $secretState") } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/extension/ComponentActivityExt.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/extension/ComponentActivityExt.kt new file mode 100644 index 00000000..68d1fbe1 --- /dev/null +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/extension/ComponentActivityExt.kt @@ -0,0 +1,67 @@ +package co.electriccoin.zcash.ui.common.extension + +import android.view.ViewGroup +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionContext +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.lifecycle.findViewTreeLifecycleOwner +import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.lifecycle.setViewTreeViewModelStoreOwner +import androidx.savedstate.findViewTreeSavedStateRegistryOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner + +/** + * Same like [ComponentActivity.setContent] but allows for addition of [ViewCompositionStrategy]. + */ +fun ComponentActivity.setContentCompat( + viewCompositionStrategy: ViewCompositionStrategy = ViewCompositionStrategy.DisposeOnDetachedFromWindow, + parent: CompositionContext? = null, + content: @Composable () -> Unit +) { + val existingComposeView = + window.decorView + .findViewById(android.R.id.content) + .getChildAt(0) as? ComposeView + + if (existingComposeView != null) { + with(existingComposeView) { + setParentCompositionContext(parent) + setContent(content) + } + } else { + ComposeView(this).apply { + // Set content and parent **before** setContentView + // to have ComposeView create the composition on attach + setViewCompositionStrategy(viewCompositionStrategy) + setParentCompositionContext(parent) + setContent(content) + // Set the view tree owners before setting the content view so that the inflation process + // and attach listeners will see them already present + setOwners() + setContentView( + this, + ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + ) + } + } +} + +private fun ComponentActivity.setOwners() { + val decorView = window.decorView + if (decorView.findViewTreeLifecycleOwner() == null) { + decorView.setViewTreeLifecycleOwner(this) + } + if (decorView.findViewTreeViewModelStoreOwner() == null) { + decorView.setViewTreeViewModelStoreOwner(this) + } + if (decorView.findViewTreeSavedStateRegistryOwner() == null) { + decorView.setViewTreeSavedStateRegistryOwner(this) + } +}