Updated on 2026-08-14
This commit is contained in:
commit
0d0cb3e871
77 changed files with 542 additions and 282 deletions
|
|
@ -1,8 +1,32 @@
|
|||
package com.tangem.tap
|
||||
|
||||
import android.content.Intent
|
||||
import android.hardware.biometrics.BiometricManager
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
|
||||
interface ActivityResultCaller {
|
||||
val activityResultLauncher: ActivityResultLauncher<Intent>?
|
||||
}
|
||||
|
||||
internal fun ActivityResultCaller.openSystemBiometrySettings() {
|
||||
val settingsAction = when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
|
||||
Settings.ACTION_BIOMETRIC_ENROLL
|
||||
}
|
||||
else -> {
|
||||
Settings.ACTION_SECURITY_SETTINGS
|
||||
}
|
||||
}
|
||||
val intent = Intent(settingsAction).apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
putExtra(
|
||||
Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
|
||||
BiometricManager.Authenticators.BIOMETRIC_STRONG,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
activityResultLauncher?.launch(intent)
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@ import com.tangem.TangemSdkLogger
|
|||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
|
|
@ -110,4 +112,8 @@ interface ApplicationEntryPoint {
|
|||
fun getDetailsFeatureToggles(): DetailsFeatureToggles
|
||||
|
||||
fun getDetailsEntryPoint(): DetailsEntryPoint
|
||||
|
||||
fun getUrlOpener(): UrlOpener
|
||||
|
||||
fun getShareManager(): ShareManager
|
||||
}
|
||||
61
app/src/main/java/com/tangem/tap/DecomposeFragment.kt
Normal file
61
app/src/main/java/com/tangem/tap/DecomposeFragment.kt
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package com.tangem.tap
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.ComposableContentComponent
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.message.EventMessageEffect
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.utils.Provider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
internal class DecomposeFragment : ComposeFragment() {
|
||||
|
||||
@Inject
|
||||
override lateinit var uiDependencies: UiDependencies
|
||||
|
||||
private val component by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
requireNotNull(componentBuilder?.build()) {
|
||||
"Component builder is not set, call newInstance() for DecomposeFragment creation first."
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
component.Content(modifier)
|
||||
|
||||
EventMessageEffect(
|
||||
messageHandler = uiDependencies.eventMessageHandler,
|
||||
snackbarHostState = uiDependencies.globalSnackbarHostState,
|
||||
)
|
||||
}
|
||||
|
||||
private class ComponentBuilder<C : ComposableContentComponent, P : Any, F : ComponentFactory<C, P>>(
|
||||
private val contextProvider: Provider<AppComponentContext>,
|
||||
private val params: P,
|
||||
private val componentFactory: F,
|
||||
) {
|
||||
|
||||
fun build(): C = componentFactory.create(contextProvider(), params)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private var componentBuilder: ComponentBuilder<*, *, *>? = null
|
||||
|
||||
fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<C, P>> newInstance(
|
||||
contextProvider: Provider<AppComponentContext>,
|
||||
params: P,
|
||||
componentFactory: F,
|
||||
): Fragment {
|
||||
this@Companion.componentBuilder = ComponentBuilder(contextProvider, params, componentFactory)
|
||||
|
||||
return DecomposeFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -298,19 +298,21 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val nfcAdapter = NfcAdapter.getDefaultAdapter(this)
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
|
||||
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE,
|
||||
)
|
||||
val intentFilters = arrayOf(
|
||||
IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED),
|
||||
IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED),
|
||||
IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED),
|
||||
)
|
||||
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, null)
|
||||
val nfcAdapter: NfcAdapter? = NfcAdapter.getDefaultAdapter(this)
|
||||
if (nfcAdapter?.isEnabled == true) {
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
|
||||
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE,
|
||||
)
|
||||
val intentFilters = arrayOf(
|
||||
IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED),
|
||||
IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED),
|
||||
IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED),
|
||||
)
|
||||
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, null)
|
||||
}
|
||||
// TODO: RESEARCH! NotificationsHandler is created in onResume and destroyed in onStop
|
||||
notificationsHandler = NotificationsHandler(binding.fragmentContainer)
|
||||
|
||||
|
|
@ -319,8 +321,11 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
val nfcAdapter = NfcAdapter.getDefaultAdapter(this)
|
||||
nfcAdapter.disableForegroundDispatch(this)
|
||||
val nfcAdapter: NfcAdapter? = NfcAdapter.getDefaultAdapter(this)
|
||||
|
||||
if (nfcAdapter?.isEnabled == true) {
|
||||
nfcAdapter.disableForegroundDispatch(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHand
|
|||
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.chat.ChatManager
|
||||
import com.tangem.tap.common.feedback.AdditionalFeedbackInfo
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
import com.tangem.tap.common.feedback.LegacyFeedbackManager
|
||||
import com.tangem.tap.common.images.createCoilImageLoader
|
||||
import com.tangem.tap.common.log.TangemLogCollector
|
||||
import com.tangem.tap.common.log.TimberFormatStrategy
|
||||
|
|
@ -181,7 +181,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
|
||||
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase
|
||||
get() = entryPoint.getSaveBlockchainErrorUseCase()
|
||||
// endregion
|
||||
|
||||
private val detailsFeatureToggles: DetailsFeatureToggles
|
||||
get() = entryPoint.getDetailsFeatureToggles()
|
||||
|
|
@ -189,6 +188,13 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
private val detailsEntryPoint: DetailsEntryPoint
|
||||
get() = entryPoint.getDetailsEntryPoint()
|
||||
|
||||
private val urlOpener
|
||||
get() = entryPoint.getUrlOpener()
|
||||
|
||||
private val shareManager
|
||||
get() = entryPoint.getShareManager()
|
||||
// endregion
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
|
|
@ -276,6 +282,8 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
saveBlockchainErrorUseCase = saveBlockchainErrorUseCase,
|
||||
detailsFeatureToggles = detailsFeatureToggles,
|
||||
detailsEntryPoint = detailsEntryPoint,
|
||||
urlOpener = urlOpener,
|
||||
shareManager = shareManager,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -363,7 +371,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
logger = if (feedbackManagerFeatureToggles.isLocalLogsEnabled) tangemSdkLogger else tangemLogCollector,
|
||||
)
|
||||
|
||||
val feedbackManager = FeedbackManager(
|
||||
val feedbackManager = LegacyFeedbackManager(
|
||||
infoHolder = additionalFeedbackInfo,
|
||||
logCollector = tangemLogCollector,
|
||||
chatManager = ChatManager(foregroundActivityObserver),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.tap.common
|
|||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.global.GlobalState
|
||||
import com.tangem.tap.common.ui.*
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -82,12 +81,12 @@ suspend fun dispatchOnMain(vararg actions: Action) {
|
|||
withMainContext { actions.forEach { store.dispatch(it) } }
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchOpenUrl(url: String) {
|
||||
dispatch(NavigationAction.OpenUrl(url))
|
||||
fun Store<AppState>.dispatchOpenUrl(url: String) {
|
||||
inject(DaggerGraphState::urlOpener).openUrl(url)
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchShare(url: String) {
|
||||
dispatch(NavigationAction.Share(url))
|
||||
fun Store<AppState>.dispatchShare(url: String) {
|
||||
inject(DaggerGraphState::shareManager).shareText(url)
|
||||
}
|
||||
|
||||
inline fun <reified T> Store<AppState>.inject(getDependency: DaggerGraphState.() -> T?): T {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.*
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import androidx.annotation.*
|
||||
|
|
@ -78,16 +81,6 @@ fun Context.getFromClipboard(default: CharSequence? = null): CharSequence? {
|
|||
return clipData.getItemAt(0).text
|
||||
}
|
||||
|
||||
fun Context.shareText(text: String) {
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_TEXT, text)
|
||||
type = "text/plain"
|
||||
}
|
||||
val shareIntent = Intent.createChooser(sendIntent, null)
|
||||
startActivity(shareIntent)
|
||||
}
|
||||
|
||||
fun View.getString(resId: Int, vararg formatArgs: Any?): String {
|
||||
return context.getString(resId, *formatArgs)
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ import java.io.StringWriter
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class FeedbackManager(
|
||||
class LegacyFeedbackManager(
|
||||
val infoHolder: AdditionalFeedbackInfo,
|
||||
private val logCollector: TangemLogCollector,
|
||||
private val chatManager: ChatManager,
|
||||
|
|
@ -144,7 +144,7 @@ class FeedbackManager(
|
|||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private companion object {
|
||||
const val DEFAULT_SUPPORT_EMAIL = "support@tangem.com"
|
||||
const val S2C_SUPPORT_EMAIL = "cardsupport@start2coin.com"
|
||||
const val FEEDBACK_FILE = "feedback.txt"
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.tap.common.feedback
|
||||
|
||||
import com.tangem.core.navigation.feedback.FeedbackManager
|
||||
import com.tangem.core.navigation.feedback.FeedbackType
|
||||
import com.tangem.tap.store
|
||||
import timber.log.Timber
|
||||
|
||||
internal class ProxyFeedbackManager : FeedbackManager {
|
||||
|
||||
override fun sendEmail(type: FeedbackType) {
|
||||
val manager = store.state.globalState.feedbackManager
|
||||
|
||||
if (manager == null) {
|
||||
Timber.e("Feedback manager is not initialized")
|
||||
return
|
||||
}
|
||||
|
||||
val data = when (type) {
|
||||
is FeedbackType.Feedback -> FeedbackEmail()
|
||||
is FeedbackType.RateCanBeBetter -> RateCanBeBetterEmail()
|
||||
is FeedbackType.ScanFails -> ScanFailsEmail()
|
||||
is FeedbackType.SendTransactionFailed -> SendTransactionFailedEmail(type.error)
|
||||
is FeedbackType.Support -> FeedbackEmail()
|
||||
}
|
||||
|
||||
manager.sendEmail(data)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.tap.common.finisher
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.tangem.core.navigation.finisher.AppFinisher
|
||||
import com.tangem.tap.MainActivity
|
||||
import com.tangem.tap.foregroundActivityObserver
|
||||
import com.tangem.tap.withForegroundActivity
|
||||
|
||||
internal class AndroidAppFinisher(
|
||||
private val appContext: Context,
|
||||
) : AppFinisher {
|
||||
|
||||
override fun finish() {
|
||||
foregroundActivityObserver.withForegroundActivity { activity ->
|
||||
activity.finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun restart() {
|
||||
val intent = Intent(appContext, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
appContext.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.common.redux
|
||||
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.TestAction
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.domain.model.WalletAddressData
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ package com.tangem.tap.common.redux.global
|
|||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.config.models.ChatConfig
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.feedback.FeedbackData
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
import com.tangem.tap.common.feedback.LegacyFeedbackManager
|
||||
import com.tangem.tap.common.redux.DebugErrorAction
|
||||
import com.tangem.tap.common.redux.ErrorAction
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
|
|
@ -79,7 +79,7 @@ sealed class GlobalAction : Action {
|
|||
|
||||
data class SetConfigManager(val configManager: ConfigManager) : GlobalAction()
|
||||
data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction()
|
||||
data class SetFeedbackManager(val feedbackManager: FeedbackManager) : GlobalAction()
|
||||
data class SetFeedbackManager(val feedbackManager: LegacyFeedbackManager) : GlobalAction()
|
||||
|
||||
data class SendEmail(val feedbackData: FeedbackData) : GlobalAction()
|
||||
data class OpenChat(val feedbackData: FeedbackData, val chatConfig: ChatConfig? = null) : GlobalAction()
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.datasource.config.models.Config
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.LogConfig
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.feedback.LegacyFeedbackManager
|
||||
import com.tangem.tap.domain.TapWalletManager
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import com.tangem.tap.features.onboarding.OnboardingManager
|
||||
|
|
@ -20,7 +20,7 @@ data class GlobalState(
|
|||
val tapWalletManager: TapWalletManager = TapWalletManager(),
|
||||
val configManager: ConfigManager? = null,
|
||||
val warningManager: WarningMessagesManager? = null,
|
||||
val feedbackManager: FeedbackManager? = null,
|
||||
val feedbackManager: LegacyFeedbackManager? = null,
|
||||
val appCurrency: AppCurrency = AppCurrency.Default,
|
||||
val scanCardFailsCounter: Int = 0,
|
||||
val dialog: StateDialog? = null,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
package com.tangem.tap.common.redux.navigation
|
||||
|
||||
import android.content.Intent
|
||||
import android.hardware.biometrics.BiometricManager
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.tap.activityResultCaller
|
||||
import com.tangem.tap.common.CustomTabsManager
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.openFragment
|
||||
import com.tangem.tap.common.extensions.popBackTo
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.Middleware
|
||||
|
|
@ -47,39 +44,11 @@ val navigationMiddleware: Middleware<AppState> = { _, state ->
|
|||
}
|
||||
}
|
||||
}
|
||||
is NavigationAction.OpenUrl -> {
|
||||
navState?.activity?.get()?.let {
|
||||
CustomTabsManager().openUrl(action.url, it)
|
||||
}
|
||||
}
|
||||
is NavigationAction.OpenDocument -> {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = action.url
|
||||
navState?.activity?.get()?.startActivity(intent)
|
||||
}
|
||||
is NavigationAction.OpenDialog -> store.dispatchDialogShow(action.stateDialog)
|
||||
is NavigationAction.OpenBiometricsSettings -> {
|
||||
val settingsAction = when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
|
||||
Settings.ACTION_BIOMETRIC_ENROLL
|
||||
}
|
||||
else -> {
|
||||
Settings.ACTION_SECURITY_SETTINGS
|
||||
}
|
||||
}
|
||||
val intent = Intent(settingsAction).apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
putExtra(
|
||||
Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
|
||||
BiometricManager.Authenticators.BIOMETRIC_STRONG,
|
||||
)
|
||||
}
|
||||
}
|
||||
activityResultCaller.activityResultLauncher?.launch(intent)
|
||||
}
|
||||
is NavigationAction.Share -> {
|
||||
navState?.activity?.get()?.shareText(action.data)
|
||||
}
|
||||
is NavigationAction.ActivityCreated,
|
||||
is NavigationAction.ActivityDestroyed,
|
||||
-> Unit
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.tap.common.share
|
||||
|
||||
import android.content.Intent
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.tap.foregroundActivityObserver
|
||||
import com.tangem.tap.withForegroundActivity
|
||||
|
||||
internal class IntentShareManager : ShareManager {
|
||||
|
||||
override fun shareText(text: String) {
|
||||
foregroundActivityObserver.withForegroundActivity { activity ->
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_TEXT, text)
|
||||
type = "text/plain"
|
||||
}
|
||||
val shareIntent = Intent.createChooser(sendIntent, null)
|
||||
|
||||
activity.startActivity(shareIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
|
|
@ -44,7 +43,7 @@ class RussianCardholdersWarningBottomSheetDialog(
|
|||
dismiss()
|
||||
}
|
||||
binding?.btnNo?.setOnClickListener {
|
||||
store.dispatch(NavigationAction.OpenUrl(INSTRUCTION_URL))
|
||||
store.dispatchOpenUrl(INSTRUCTION_URL)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
import com.tangem.tap.common.feedback.ScanFailsEmail
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.tap.common
|
||||
package com.tangem.tap.common.url
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
|
|
@ -8,12 +8,22 @@ import androidx.browser.customtabs.CustomTabColorSchemeParams
|
|||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK
|
||||
import androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
|
||||
import com.tangem.tap.common.extensions.getColorCompat
|
||||
import com.tangem.tap.foregroundActivityObserver
|
||||
import com.tangem.tap.withForegroundActivity
|
||||
import com.tangem.wallet.R
|
||||
|
||||
class CustomTabsManager {
|
||||
fun openUrl(url: String, context: Context) {
|
||||
internal class CustomTabsUrlOpener : UrlOpener {
|
||||
|
||||
override fun openUrl(url: String) {
|
||||
foregroundActivityObserver.withForegroundActivity {
|
||||
openUrl(url, context = it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openUrl(url: String, context: Context) {
|
||||
if (url.isEmpty()) return
|
||||
val customTabsIntent = CustomTabsIntent.Builder()
|
||||
.setDefaultColorSchemeParams(
|
||||
|
|
@ -6,10 +6,8 @@ import com.tangem.domain.exchange.RampStateManager
|
|||
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
|
||||
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
|
||||
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
|
||||
import com.tangem.feature.tester.ActivityClassWrapper
|
||||
import com.tangem.tap.MainActivity
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.network.exchangeServices.DefaultRampManager
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import dagger.Module
|
||||
|
|
@ -67,10 +65,4 @@ internal object ActivityModule {
|
|||
): GetPolkadotCheckHasImmortalUseCase {
|
||||
return GetPolkadotCheckHasImmortalUseCase(polkadotAccountHealthCheckRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideActivityClassWrapper(): ActivityClassWrapper {
|
||||
return ActivityClassWrapper(MainActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,7 @@ import com.tangem.core.decompose.context.AppComponentContext
|
|||
import com.tangem.core.decompose.context.DefaultAppComponentContext
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.RootAppComponentContext
|
||||
import com.tangem.core.decompose.ui.UiMessage
|
||||
import com.tangem.core.decompose.ui.UiMessageHandler
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -16,7 +15,6 @@ import dagger.hilt.InstallIn
|
|||
import dagger.hilt.android.components.ActivityComponent
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import timber.log.Timber
|
||||
|
||||
@Module
|
||||
@InstallIn(ActivityComponent::class)
|
||||
|
|
@ -29,17 +27,11 @@ internal object RootAppComponentContextModule {
|
|||
@ActivityContext context: Context,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
componentBuilder: DecomposeComponent.Builder,
|
||||
uiDependencies: UiDependencies,
|
||||
): AppComponentContext {
|
||||
// TODO: Implement message handler
|
||||
val dummyMessageHandler = object : UiMessageHandler {
|
||||
override fun handleMessage(message: UiMessage) {
|
||||
Timber.w("Unable to handle message: $message")
|
||||
}
|
||||
}
|
||||
|
||||
return DefaultAppComponentContext(
|
||||
componentContext = (context as AppCompatActivity).defaultComponentContext(),
|
||||
messageHandler = dummyMessageHandler,
|
||||
messageHandler = uiDependencies.eventMessageHandler,
|
||||
dispatchers = dispatchers,
|
||||
hiltComponentBuilder = componentBuilder,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.haptic.HapticManager
|
||||
import com.tangem.core.ui.message.EventMessageHandler
|
||||
import com.tangem.core.ui.theme.AppThemeModeHolder
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -19,6 +21,8 @@ internal object UiDependenciesModule {
|
|||
return object : UiDependencies {
|
||||
override val hapticManager = hapticManager
|
||||
override val appThemeModeHolder = appThemeModeHolder
|
||||
override val globalSnackbarHostState: SnackbarHostState = SnackbarHostState()
|
||||
override val eventMessageHandler: EventMessageHandler = EventMessageHandler()
|
||||
}
|
||||
}
|
||||
}
|
||||
38
app/src/main/java/com/tangem/tap/di/UtilsModule.kt
Normal file
38
app/src/main/java/com/tangem/tap/di/UtilsModule.kt
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.core.navigation.feedback.FeedbackManager
|
||||
import com.tangem.core.navigation.finisher.AppFinisher
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.tap.common.feedback.ProxyFeedbackManager
|
||||
import com.tangem.tap.common.finisher.AndroidAppFinisher
|
||||
import com.tangem.tap.common.share.IntentShareManager
|
||||
import com.tangem.tap.common.url.CustomTabsUrlOpener
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object UtilsModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideShareManager(): ShareManager = IntentShareManager()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUrlOpener(): UrlOpener = CustomTabsUrlOpener()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFeedbackManager(): FeedbackManager = ProxyFeedbackManager()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppFinisher(@ApplicationContext context: Context): AppFinisher = AndroidAppFinisher(context)
|
||||
}
|
||||
|
|
@ -8,9 +8,9 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.domain.card.ScanCardException
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
|||
import com.tangem.domain.wallets.legacy.UserWalletsListError
|
||||
import com.tangem.domain.wallets.legacy.asLockable
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Settings
|
||||
import com.tangem.tap.common.extensions.*
|
||||
|
|
@ -30,9 +31,6 @@ import com.tangem.tap.features.demo.DemoHelper
|
|||
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -273,7 +271,7 @@ class DetailsMiddleware {
|
|||
|
||||
private fun enrollBiometrics() {
|
||||
Analytics.send(Settings.AppSettings.ButtonEnableBiometricAuthentication)
|
||||
store.dispatchOnMain(NavigationAction.OpenBiometricsSettings)
|
||||
activityResultCaller.openSystemBiometrySettings()
|
||||
}
|
||||
|
||||
private fun changeAppThemeMode(appThemeMode: AppThemeMode) {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcSignMessage
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import com.tangem.domain.wallets.repository.WalletsRepository
|
|||
import com.tangem.tap.common.analytics.events.Settings
|
||||
import com.tangem.tap.common.extensions.addContext
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
import com.tangem.tap.common.feedback.FeedbackEmail
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -179,7 +180,7 @@ internal class DetailsViewModel(
|
|||
|
||||
private fun handleSocialNetworkClick(link: SocialNetworkLink) {
|
||||
Analytics.send(Settings.ButtonSocialNetwork(link.network))
|
||||
store.dispatchOnMain(NavigationAction.OpenUrl(link.url))
|
||||
store.dispatchOpenUrl(link.url)
|
||||
}
|
||||
|
||||
private fun getSocialLinks(): ImmutableList<SocialNetworkLink> {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.features.onboarding
|
||||
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.tap.features.onboarding.products.wallet.redux
|
|||
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import org.rekotlin.StateType
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.core.navigation.AppScreen
|
|||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.MainScreen
|
||||
import com.tangem.tap.common.analytics.events.Onboarding
|
||||
|
|
@ -16,11 +17,7 @@ import com.tangem.tap.common.extensions.dispatchWithMain
|
|||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -88,7 +85,7 @@ internal class SaveWalletMiddleware {
|
|||
}
|
||||
|
||||
private fun enrollBiometrics() {
|
||||
store.dispatchOnMain(NavigationAction.OpenBiometricsSettings)
|
||||
activityResultCaller.openSystemBiometrySettings()
|
||||
}
|
||||
|
||||
private fun allowToUseBiometrics(state: SaveWalletState) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.blockchain.common.FeePaidCurrency
|
|||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered
|
||||
import com.tangem.tap.common.redux.ToastNotificationAction
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import com.tangem.blockchain.common.Amount
|
|||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.tap.common.CurrencyConverter
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import com.tangem.core.navigation.ReduxNavController
|
|||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.feedback.FeedbackEmail
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.network.exchangeServices.ExchangeService
|
||||
import org.rekotlin.Action
|
||||
|
|
@ -57,7 +57,7 @@ class AppStateHolder @Inject constructor() : ReduxNavController, ReduxStateHolde
|
|||
mainStore?.onUserWalletSelected(userWallet)
|
||||
}
|
||||
|
||||
override fun sendFeedbackEmail() {
|
||||
mainStore?.dispatch(GlobalAction.SendEmail(FeedbackEmail()))
|
||||
override fun dispatchDialogShow(dialog: StateDialog) {
|
||||
mainStore?.dispatchDialogShow(dialog)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ package com.tangem.tap.proxy.redux
|
|||
import com.tangem.TangemSdkLogger
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.core.navigation.email.EmailSender
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
|
||||
|
|
@ -34,8 +36,8 @@ import com.tangem.features.staking.api.navigation.StakingRouter
|
|||
import com.tangem.features.tester.api.TesterRouter
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
|
||||
import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
|
|
@ -81,4 +83,6 @@ data class DaggerGraphState(
|
|||
val detailsFeatureToggles: DetailsFeatureToggles? = null,
|
||||
val detailsEntryPoint: DetailsEntryPoint? = null,
|
||||
val stakingRouter: StakingRouter? = null,
|
||||
val urlOpener: UrlOpener? = null,
|
||||
val shareManager: ShareManager? = null,
|
||||
) : StateType
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.decompose.factory
|
||||
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
|
||||
interface ComponentFactory<Component : Any, Params : Any> {
|
||||
|
||||
fun create(context: AppComponentContext, params: Params): Component
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.arkivanov.decompose.router.stack.pop
|
|||
import com.arkivanov.decompose.router.stack.popWhile
|
||||
import com.arkivanov.decompose.router.stack.pushNew
|
||||
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal class DefaultRouter(
|
||||
private val navigationProvider: AppNavigationProvider,
|
||||
|
|
@ -19,6 +20,15 @@ internal class DefaultRouter(
|
|||
navigation.pushNew(route, onComplete)
|
||||
}
|
||||
|
||||
override fun replaceAll(vararg routes: Route, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
val newRoutes = routes.toList()
|
||||
|
||||
navigation.navigate(
|
||||
transformer = { newRoutes },
|
||||
onComplete = { newStack, _ -> onComplete(newStack.size == newRoutes.size) },
|
||||
)
|
||||
}
|
||||
|
||||
override fun pop(onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
navigation.pop(onComplete)
|
||||
}
|
||||
|
|
@ -29,4 +39,11 @@ internal class DefaultRouter(
|
|||
onComplete = onComplete,
|
||||
)
|
||||
}
|
||||
|
||||
override fun popTo(routeClass: KClass<out Route>, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
navigation.popWhile(
|
||||
predicate = { it::class != routeClass },
|
||||
onComplete = onComplete,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.core.decompose.navigation
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class DummyRouter : Router {
|
||||
|
||||
override fun push(route: Route, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
onComplete(true)
|
||||
}
|
||||
|
||||
override fun replaceAll(vararg routes: Route, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
onComplete(true)
|
||||
}
|
||||
|
||||
override fun pop(onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
onComplete(true)
|
||||
}
|
||||
|
||||
override fun popTo(route: Route, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
onComplete(true)
|
||||
}
|
||||
|
||||
override fun popTo(routeClass: KClass<out Route>, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
onComplete(true)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.core.decompose.navigation
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Interface for a router in the application.
|
||||
* It provides methods for navigating through the application.
|
||||
* It provides methods for navigating through the application by stack.
|
||||
*/
|
||||
interface Router {
|
||||
|
||||
|
|
@ -14,6 +16,14 @@ interface Router {
|
|||
*/
|
||||
fun push(route: Route, onComplete: (isSuccess: Boolean) -> Unit = {})
|
||||
|
||||
/**
|
||||
* Replaces ***all*** routes in the navigation stack with the specified [routes].
|
||||
*
|
||||
* @param routes The routes to replace the current stack with.
|
||||
* @param onComplete The callback to be invoked when the operation is complete.
|
||||
*/
|
||||
fun replaceAll(vararg routes: Route, onComplete: (isSuccess: Boolean) -> Unit = {})
|
||||
|
||||
/**
|
||||
* Pops the top route from the navigation stack.
|
||||
*
|
||||
|
|
@ -22,10 +32,18 @@ interface Router {
|
|||
fun pop(onComplete: (isSuccess: Boolean) -> Unit = {})
|
||||
|
||||
/**
|
||||
* Pops routes from the navigation stack until the specified route is found.
|
||||
* Pops routes from the navigation stack until the specified [route] is found.
|
||||
*
|
||||
* @param route The route to pop to.
|
||||
* @param onComplete The callback to be invoked when the operation is complete.
|
||||
*/
|
||||
fun popTo(route: Route, onComplete: (isSuccess: Boolean) -> Unit = {})
|
||||
|
||||
/**
|
||||
* Pops routes from the navigation stack until the ***first*** specified [routeClass] is found.
|
||||
*
|
||||
* @param routeClass The route class to pop to.
|
||||
* @param onComplete The callback to be invoked when the operation is complete.
|
||||
*/
|
||||
fun popTo(routeClass: KClass<out Route>, onComplete: (isSuccess: Boolean) -> Unit = {})
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.core.decompose.navigation
|
||||
|
||||
/**
|
||||
* Pops routes from the navigation stack until the specified route [R] is found.
|
||||
*
|
||||
* @param R The route to pop to.
|
||||
* @param onComplete The callback to be invoked when the operation is complete.
|
||||
*/
|
||||
inline fun <reified R : Route> Router.popTo(noinline onComplete: (isSuccess: Boolean) -> Unit = {}) {
|
||||
popTo(R::class, onComplete)
|
||||
}
|
||||
|
|
@ -17,16 +17,8 @@ sealed class NavigationAction : Action {
|
|||
|
||||
data class PopBackTo(val screen: AppScreen? = null, val inclusive: Boolean = false) : NavigationAction()
|
||||
|
||||
data class OpenUrl(val url: String) : NavigationAction()
|
||||
|
||||
data class OpenDocument(val url: Uri) : NavigationAction()
|
||||
|
||||
object OpenBiometricsSettings : NavigationAction()
|
||||
|
||||
data class OpenDialog(val stateDialog: StateDialog) : NavigationAction()
|
||||
|
||||
data class Share(val data: String) : NavigationAction()
|
||||
|
||||
data class ActivityCreated(val activity: WeakReference<AppCompatActivity>) : NavigationAction()
|
||||
|
||||
data class ActivityDestroyed(val activity: WeakReference<AppCompatActivity>) : NavigationAction()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.navigation.feedback
|
||||
|
||||
class DummyFeedbackManager : FeedbackManager {
|
||||
|
||||
override fun sendEmail(type: FeedbackType) {
|
||||
/* no-op */
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.navigation.feedback
|
||||
|
||||
interface FeedbackManager {
|
||||
|
||||
fun sendEmail(type: FeedbackType)
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.core.navigation.feedback
|
||||
|
||||
sealed class FeedbackType {
|
||||
data object RateCanBeBetter : FeedbackType()
|
||||
|
||||
data object ScanFails : FeedbackType()
|
||||
|
||||
data class SendTransactionFailed(val error: String) : FeedbackType()
|
||||
|
||||
data object Feedback : FeedbackType()
|
||||
|
||||
data object Support : FeedbackType()
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.navigation.finisher
|
||||
|
||||
interface AppFinisher {
|
||||
|
||||
fun finish()
|
||||
|
||||
fun restart()
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.navigation.share
|
||||
|
||||
class DummyShareManager : ShareManager {
|
||||
|
||||
override fun shareText(text: String) {
|
||||
/* no-op */
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.navigation.share
|
||||
|
||||
interface ShareManager {
|
||||
|
||||
fun shareText(text: String)
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.navigation.url
|
||||
|
||||
class DummyUrlOpener : UrlOpener {
|
||||
|
||||
override fun openUrl(url: String) {
|
||||
/* no-op */
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.navigation.url
|
||||
|
||||
interface UrlOpener {
|
||||
|
||||
fun openUrl(url: String)
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import androidx.compose.runtime.Stable
|
|||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Stable
|
||||
fun interface ComposableContentProvider {
|
||||
fun interface ComposableContentComponent {
|
||||
|
||||
@Composable
|
||||
@Suppress("TopLevelComposableFunctions") // TODO: Remove this check
|
||||
|
|
@ -1,11 +1,19 @@
|
|||
package com.tangem.core.ui
|
||||
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.ui.haptic.HapticManager
|
||||
import com.tangem.core.ui.message.EventMessageHandler
|
||||
import com.tangem.core.ui.theme.AppThemeModeHolder
|
||||
|
||||
@Stable
|
||||
interface UiDependencies {
|
||||
|
||||
val hapticManager: HapticManager
|
||||
|
||||
val appThemeModeHolder: AppThemeModeHolder
|
||||
|
||||
val globalSnackbarHostState: SnackbarHostState
|
||||
|
||||
val eventMessageHandler: EventMessageHandler
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.core.ui.message
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.decompose.ui.UiMessage
|
||||
import com.tangem.core.decompose.ui.UiMessageHandler
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
|
|
@ -11,6 +12,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
/**
|
||||
* Message handler that is used to show or remove an [EventMessage] in the UI.
|
||||
*/
|
||||
@Stable
|
||||
class EventMessageHandler(
|
||||
private val events: MutableStateFlow<StateEvent<EventMessage>> = MutableStateFlow(consumedEvent()),
|
||||
) : UiMessageHandler, StateFlow<StateEvent<EventMessage>> by events {
|
||||
|
|
|
|||
|
|
@ -3,19 +3,18 @@ package com.tangem.core.ui.res
|
|||
import androidx.compose.material.Colors
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.ProvideTextStyle
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.*
|
||||
import com.tangem.core.ui.haptic.HapticManager
|
||||
import com.tangem.core.ui.haptic.MockHapticManager
|
||||
|
||||
// TODO: use isSystemInDarkTheme() for automatic color detection
|
||||
internal const val IS_SYSTEM_IN_DARK_THEME: Boolean = false
|
||||
|
||||
@Composable
|
||||
fun TangemTheme(
|
||||
isDark: Boolean = false,
|
||||
typography: TangemTypography = TangemTheme.typography,
|
||||
dimens: TangemDimens = TangemTheme.dimens,
|
||||
hapticManager: HapticManager = MockHapticManager,
|
||||
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val themeColors = if (isDark) darkThemeColors() else lightThemeColors()
|
||||
|
|
@ -33,6 +32,7 @@ fun TangemTheme(
|
|||
LocalTangemShapes provides shapes,
|
||||
LocalIsInDarkTheme provides isDark,
|
||||
LocalHapticManager provides hapticManager,
|
||||
LocalSnackbarHostState provides snackbarHostState,
|
||||
) {
|
||||
ProvideTextStyle(
|
||||
value = TangemTheme.typography.body1,
|
||||
|
|
@ -204,4 +204,8 @@ val LocalIsInDarkTheme = staticCompositionLocalOf { false }
|
|||
|
||||
val LocalHapticManager = staticCompositionLocalOf<HapticManager> {
|
||||
error("No HapticManager provided")
|
||||
}
|
||||
|
||||
val LocalSnackbarHostState = staticCompositionLocalOf<SnackbarHostState> {
|
||||
error("No SnackbarHostState provided")
|
||||
}
|
||||
|
|
@ -58,6 +58,7 @@ internal fun ComposeScreen.createComposeView(context: Context): ComposeView {
|
|||
TangemTheme(
|
||||
isDark = shouldUseDarkTheme(appThemeMode),
|
||||
hapticManager = uiDependencies.hapticManager,
|
||||
snackbarHostState = uiDependencies.globalSnackbarHostState,
|
||||
) {
|
||||
ScreenContent(modifier = screenModifier)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,5 +11,5 @@ interface ReduxStateHolder {
|
|||
|
||||
suspend fun onUserWalletSelected(userWallet: UserWallet)
|
||||
|
||||
fun sendFeedbackEmail()
|
||||
fun dispatchDialogShow(dialog: StateDialog)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.core.navigation
|
||||
package com.tangem.domain.redux
|
||||
|
||||
interface StateDialog {
|
||||
|
||||
|
|
@ -4,16 +4,20 @@ import com.tangem.core.decompose.navigation.Route
|
|||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.core.navigation.feedback.FeedbackManager
|
||||
import com.tangem.core.navigation.feedback.FeedbackType
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.features.details.routing.DetailsRoute
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
import javax.inject.Inject
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
// TODO: Remove after [REDACTED_JIRA]
|
||||
internal class DetailsRouter @Inject constructor(
|
||||
private val reduxNavController: ReduxNavController,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val feedbackManager: FeedbackManager,
|
||||
private val testerRouter: TesterRouter,
|
||||
private val urlOpener: UrlOpener,
|
||||
) : Router {
|
||||
|
||||
override fun push(route: Route, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
|
|
@ -23,13 +27,13 @@ internal class DetailsRouter @Inject constructor(
|
|||
reduxNavController.navigate(NavigationAction.NavigateTo(route.screen, bundle = route.params))
|
||||
}
|
||||
is DetailsRoute.Feedback -> {
|
||||
reduxStateHolder.sendFeedbackEmail()
|
||||
feedbackManager.sendEmail(FeedbackType.Feedback)
|
||||
}
|
||||
is DetailsRoute.TesterMenu -> {
|
||||
testerRouter.startTesterScreen()
|
||||
}
|
||||
is DetailsRoute.Url -> {
|
||||
reduxNavController.navigate(NavigationAction.OpenUrl(route.url))
|
||||
urlOpener.openUrl(route.url)
|
||||
}
|
||||
}
|
||||
onComplete(true)
|
||||
|
|
@ -43,13 +47,15 @@ internal class DetailsRouter @Inject constructor(
|
|||
onComplete(true)
|
||||
}
|
||||
|
||||
override fun popTo(routeClass: KClass<out Route>, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
TODO("This class will be removed in future")
|
||||
}
|
||||
|
||||
override fun popTo(route: Route, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
if (route is DetailsRoute.Screen) {
|
||||
reduxNavController.popBackStack(route.screen)
|
||||
onComplete(true)
|
||||
} else {
|
||||
reduxNavController.getBackStack()
|
||||
onComplete(false)
|
||||
}
|
||||
TODO("This class will be removed in future")
|
||||
}
|
||||
|
||||
override fun replaceAll(vararg routes: Route, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
TODO("This class will be removed in future")
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,10 @@ package com.tangem.features.details.component
|
|||
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.ui.ComposableContentProvider
|
||||
import com.tangem.core.ui.ComposableContentComponent
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface DetailsComponent : ComposableContentProvider {
|
||||
interface DetailsComponent : ComposableContentComponent {
|
||||
|
||||
val snackbarHostState: SnackbarHostState
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.features.details.component
|
||||
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.ui.ComposableContentProvider
|
||||
import com.tangem.core.ui.ComposableContentComponent
|
||||
|
||||
interface UserWalletListComponent : ComposableContentProvider {
|
||||
interface UserWalletListComponent : ComposableContentComponent {
|
||||
|
||||
interface Factory {
|
||||
fun create(context: AppComponentContext): UserWalletListComponent
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.details.component.preview
|
|||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.decompose.navigation.DummyRouter
|
||||
import com.tangem.features.details.component.DetailsComponent
|
||||
import com.tangem.features.details.entity.DetailsFooterUM
|
||||
import com.tangem.features.details.entity.DetailsUM
|
||||
|
|
@ -17,12 +18,12 @@ internal class PreviewDetailsComponent : DetailsComponent {
|
|||
|
||||
private val previewBlocks = runBlocking {
|
||||
ItemsBuilder(
|
||||
router = PreviewRouter(),
|
||||
router = DummyRouter(),
|
||||
).buldAll(isWalletConnectAvailable = true)
|
||||
}
|
||||
|
||||
private val previewFooter = DetailsFooterUM(
|
||||
socials = SocialsBuilder(PreviewRouter()).buildAll(),
|
||||
socials = SocialsBuilder(DummyRouter()).buildAll(),
|
||||
appVersion = "1.0.0-preview",
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.features.details.component.preview
|
||||
|
||||
import com.tangem.core.decompose.navigation.Route
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
|
||||
internal class PreviewRouter : Router {
|
||||
override fun push(route: Route, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
override fun pop(onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
override fun popTo(route: Route, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
/* no-op */
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ import androidx.compose.ui.res.painterResource
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.ComposableContentProvider
|
||||
import com.tangem.core.ui.ComposableContentComponent
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.components.snackbar.TangemSnackbarHost
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -36,7 +36,7 @@ private const val COLLAPSED_APP_BAR_THRESHOLD = 0.4f
|
|||
internal fun DetailsScreen(
|
||||
state: DetailsUM,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
userWalletListBlockContent: ComposableContentProvider,
|
||||
userWalletListBlockContent: ComposableContentComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val backgroundColor = TangemTheme.colors.background.secondary
|
||||
|
|
@ -118,7 +118,7 @@ private fun TopBar(state: DetailsUM, scrollBehavior: TopAppBarScrollBehavior, mo
|
|||
@Composable
|
||||
private fun Content(
|
||||
state: DetailsUM,
|
||||
userWalletListBlockContent: ComposableContentProvider,
|
||||
userWalletListBlockContent: ComposableContentComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(
|
||||
|
|
@ -152,7 +152,7 @@ private fun Content(
|
|||
@Composable
|
||||
private fun Block(
|
||||
model: DetailsItemUM,
|
||||
userWalletListBlockContent: ComposableContentProvider,
|
||||
userWalletListBlockContent: ComposableContentComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.isNullOrEmpty
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -22,7 +21,6 @@ import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
|
|||
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.SelectWalletUseCase
|
||||
import com.tangem.features.details.impl.R
|
||||
import com.tangem.features.details.routing.DetailsRoute
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
|
|
@ -44,7 +42,7 @@ internal class UserWalletSaver @Inject constructor(
|
|||
|
||||
saveWallet(userWallet)
|
||||
|
||||
router.popTo(DetailsRoute.Screen(AppScreen.Wallet))
|
||||
// router.popTo(DetailsRoute.Screen(AppScreen.Wallet))
|
||||
},
|
||||
recover = { error ->
|
||||
val message = error.message
|
||||
|
|
@ -77,7 +75,7 @@ internal class UserWalletSaver @Inject constructor(
|
|||
selectWalletUseCase(userWallet.walletId).bind()
|
||||
}
|
||||
|
||||
router.popTo(DetailsRoute.Screen(AppScreen.Wallet))
|
||||
// router.popTo(DetailsRoute.Screen(AppScreen.Wallet))
|
||||
}
|
||||
|
||||
private suspend fun Raise<Error>.createUserWallet(response: ScanResponse): UserWallet {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ dependencies {
|
|||
|
||||
/** Compose libraries */
|
||||
implementation(deps.compose.material)
|
||||
implementation(deps.compose.material3)
|
||||
implementation(deps.compose.animation)
|
||||
implementation(deps.compose.foundation)
|
||||
implementation(deps.compose.ui)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.send.impl.di
|
||||
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.features.send.api.navigation.SendRouter
|
||||
import com.tangem.features.send.impl.navigation.DefaultSendRouter
|
||||
import dagger.Module
|
||||
|
|
@ -18,7 +19,7 @@ internal object SendRouterModule {
|
|||
|
||||
@Provides
|
||||
@ActivityScoped
|
||||
fun provideSendRouter(reduxNavController: ReduxNavController): SendRouter {
|
||||
return DefaultSendRouter(reduxNavController)
|
||||
fun provideSendRouter(reduxNavController: ReduxNavController, urlOpener: UrlOpener): SendRouter {
|
||||
return DefaultSendRouter(reduxNavController, urlOpener)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import androidx.fragment.app.Fragment
|
|||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
|
@ -14,12 +15,13 @@ import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
|||
|
||||
internal class DefaultSendRouter(
|
||||
private val reduxNavController: ReduxNavController,
|
||||
private val urlOpener: UrlOpener,
|
||||
) : InnerSendRouter {
|
||||
|
||||
override fun getEntryFragment(): Fragment = SendFragment.create()
|
||||
|
||||
override fun openUrl(url: String) {
|
||||
reduxNavController.navigate(NavigationAction.OpenUrl(url = url))
|
||||
urlOpener.openUrl(url)
|
||||
}
|
||||
|
||||
override fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) {
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
package com.tangem.features.tester.api
|
||||
|
||||
/**
|
||||
* Interface for app restarter
|
||||
*/
|
||||
interface AppRestarter {
|
||||
|
||||
fun restart()
|
||||
}
|
||||
|
|
@ -39,6 +39,7 @@ dependencies {
|
|||
implementation(projects.core.featuretoggles)
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.navigation)
|
||||
|
||||
/** Feature Apis */
|
||||
implementation(projects.features.tester.api)
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.feature.tester
|
||||
|
||||
import android.app.Activity
|
||||
|
||||
/**
|
||||
* Wraps the main activity class to avoid type erasure issues during injection.
|
||||
*
|
||||
* @property clazz activity class
|
||||
*/
|
||||
class ActivityClassWrapper(
|
||||
val clazz: Class<out Activity>,
|
||||
)
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package com.tangem.feature.tester.apprestarter
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.tangem.feature.tester.ActivityClassWrapper
|
||||
import com.tangem.features.tester.api.AppRestarter
|
||||
|
||||
/**
|
||||
* Entity that kills the process and restarts the main activity
|
||||
* @property context Activity context
|
||||
*/
|
||||
internal class DefaultAppRestarter(
|
||||
private val context: Context,
|
||||
private val activityClassWrapper: ActivityClassWrapper,
|
||||
) : AppRestarter {
|
||||
|
||||
override fun restart() {
|
||||
if (context !is Activity) return
|
||||
|
||||
context.finish()
|
||||
context.startActivity(
|
||||
Intent(context, activityClassWrapper.clazz).apply { flags = Intent.FLAG_ACTIVITY_CLEAR_TOP },
|
||||
)
|
||||
Runtime.getRuntime().exit(0)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package com.tangem.feature.tester.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.feature.tester.ActivityClassWrapper
|
||||
import com.tangem.feature.tester.apprestarter.DefaultAppRestarter
|
||||
import com.tangem.features.tester.api.AppRestarter
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ActivityComponent
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
|
||||
@Module
|
||||
@InstallIn(ActivityComponent::class)
|
||||
internal object RestarterModule {
|
||||
|
||||
@Provides
|
||||
@ActivityScoped
|
||||
fun provideAppRestarter(
|
||||
@ActivityContext context: Context,
|
||||
activityClassWrapper: ActivityClassWrapper,
|
||||
): AppRestarter {
|
||||
return DefaultAppRestarter(context, activityClassWrapper)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.tangem.core.navigation.finisher.AppFinisher
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -18,7 +19,6 @@ import com.tangem.feature.tester.presentation.menu.state.TesterMenuContentState
|
|||
import com.tangem.feature.tester.presentation.menu.ui.TesterMenuScreen
|
||||
import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter
|
||||
import com.tangem.feature.tester.presentation.navigation.TesterScreen
|
||||
import com.tangem.features.tester.api.AppRestarter
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
|
@ -35,7 +35,7 @@ internal class TesterActivity : ComposeActivity() {
|
|||
lateinit var testerRouter: TesterRouter
|
||||
|
||||
@Inject
|
||||
lateinit var appRestarter: AppRestarter
|
||||
lateinit var appFinisher: AppFinisher
|
||||
|
||||
private val innerTesterRouter: InnerTesterRouter
|
||||
get() = requireNotNull(testerRouter as? InnerTesterRouter) {
|
||||
|
|
@ -70,7 +70,7 @@ internal class TesterActivity : ComposeActivity() {
|
|||
|
||||
composable(route = TesterScreen.FEATURE_TOGGLES.name) {
|
||||
val viewModel = hiltViewModel<FeatureTogglesViewModel>().apply {
|
||||
setupInteractions(innerTesterRouter, appRestarter)
|
||||
setupInteractions(innerTesterRouter, appFinisher)
|
||||
}
|
||||
|
||||
FeatureTogglesScreen(state = viewModel.uiState)
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.core.featuretoggle.manager.MutableFeatureTogglesManager
|
||||
import com.tangem.core.navigation.finisher.AppFinisher
|
||||
import com.tangem.feature.tester.presentation.featuretoggles.models.TesterFeatureToggle
|
||||
import com.tangem.feature.tester.presentation.featuretoggles.state.FeatureTogglesContentState
|
||||
import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter
|
||||
import com.tangem.features.tester.api.AppRestarter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -40,10 +40,10 @@ internal class FeatureTogglesViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
/** Setup navigation state property by router [router] and provides app restart method by [appRestarter] */
|
||||
fun setupInteractions(router: InnerTesterRouter, appRestarter: AppRestarter) {
|
||||
fun setupInteractions(router: InnerTesterRouter, appFinisher: AppFinisher) {
|
||||
uiState = uiState.copy(
|
||||
onBackClick = router::back,
|
||||
onApplyChangesClick = appRestarter::restart,
|
||||
onApplyChangesClick = appFinisher::restart,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.feature.tokendetails.di
|
||||
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.feature.tokendetails.presentation.router.DefaultTokenDetailsRouter
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import dagger.Module
|
||||
|
|
@ -15,7 +17,11 @@ internal object TokenDetailsRouterModule {
|
|||
|
||||
@Provides
|
||||
@ActivityScoped
|
||||
fun provideTokenDetailsRouter(reduxNavController: ReduxNavController): TokenDetailsRouter {
|
||||
return DefaultTokenDetailsRouter(reduxNavController)
|
||||
fun provideTokenDetailsRouter(
|
||||
reduxNavController: ReduxNavController,
|
||||
urlOpener: UrlOpener,
|
||||
shareManager: ShareManager,
|
||||
): TokenDetailsRouter {
|
||||
return DefaultTokenDetailsRouter(reduxNavController, urlOpener, shareManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@ import androidx.fragment.app.Fragment
|
|||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.tokendetails.presentation.TokenDetailsFragment
|
||||
|
|
@ -12,6 +14,8 @@ import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
|||
|
||||
internal class DefaultTokenDetailsRouter(
|
||||
private val reduxNavController: ReduxNavController,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val shareManager: ShareManager,
|
||||
) : InnerTokenDetailsRouter {
|
||||
|
||||
override fun getEntryFragment(): Fragment = TokenDetailsFragment()
|
||||
|
|
@ -21,11 +25,11 @@ internal class DefaultTokenDetailsRouter(
|
|||
}
|
||||
|
||||
override fun openUrl(url: String) {
|
||||
reduxNavController.navigate(NavigationAction.OpenUrl(url = url))
|
||||
urlOpener.openUrl(url)
|
||||
}
|
||||
|
||||
override fun share(text: String) {
|
||||
reduxNavController.navigate(NavigationAction.Share(text))
|
||||
shareManager.shareText(text)
|
||||
}
|
||||
|
||||
override fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.feature.wallet.di
|
||||
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.feature.wallet.presentation.router.DefaultWalletRouter
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
import dagger.Module
|
||||
|
|
@ -15,7 +17,11 @@ internal object WalletRouterModule {
|
|||
|
||||
@Provides
|
||||
@ActivityScoped
|
||||
fun provideWalletRouter(reduxNavController: ReduxNavController): WalletRouter {
|
||||
return DefaultWalletRouter(reduxNavController = reduxNavController)
|
||||
fun provideWalletRouter(
|
||||
reduxNavController: ReduxNavController,
|
||||
reduxStateHolder: ReduxStateHolder,
|
||||
urlOpener: UrlOpener,
|
||||
): WalletRouter {
|
||||
return DefaultWalletRouter(reduxNavController, reduxStateHolder, urlOpener)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,9 @@ import androidx.navigation.navArgument
|
|||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.onboarding.navigation.OnboardingRouter
|
||||
|
|
@ -37,6 +39,8 @@ import kotlin.properties.Delegates
|
|||
/** Default implementation of wallet feature router */
|
||||
internal class DefaultWalletRouter(
|
||||
private val reduxNavController: ReduxNavController,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val urlOpener: UrlOpener,
|
||||
) : InnerWalletRouter {
|
||||
|
||||
private var navController: NavHostController by Delegates.notNull()
|
||||
|
|
@ -138,7 +142,7 @@ internal class DefaultWalletRouter(
|
|||
}
|
||||
|
||||
override fun openUrl(url: String) {
|
||||
reduxNavController.navigate(action = NavigationAction.OpenUrl(url))
|
||||
urlOpener.openUrl(url)
|
||||
}
|
||||
|
||||
override fun openTokenDetails(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) {
|
||||
|
|
@ -171,11 +175,7 @@ internal class DefaultWalletRouter(
|
|||
}
|
||||
|
||||
override fun openScanFailedDialog() {
|
||||
reduxNavController.navigate(
|
||||
action = NavigationAction.OpenDialog(
|
||||
StateDialog.ScanFailsDialog(StateDialog.ScanFailsSource.MAIN),
|
||||
),
|
||||
)
|
||||
reduxStateHolder.dispatchDialogShow(StateDialog.ScanFailsDialog(StateDialog.ScanFailsSource.MAIN))
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue