diff --git a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt index 5099008e5d..83157a1231 100644 --- a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt +++ b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt @@ -15,15 +15,17 @@ import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.feedback.FeedbackManagerFeatureToggles +import com.tangem.domain.feedback.GetFeedbackEmailUseCase +import com.tangem.domain.feedback.SaveBlockchainErrorUseCase import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository -import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.repository.WalletsRepository +import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository @@ -98,4 +100,8 @@ interface ApplicationEntryPoint { fun getSettingsRepository(): SettingsRepository fun getBlockchainSDKFactory(): BlockchainSDKFactory + + fun getGetFeedbackEmailUseCase(): GetFeedbackEmailUseCase + + fun getSaveBlockchainErrorUseCase(): SaveBlockchainErrorUseCase } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 83dd77c841..78b55f9ecb 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -30,6 +30,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.deeplink.DeepLinksRegistry import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction +import com.tangem.core.navigation.email.EmailSender import com.tangem.core.ui.event.StateEvent import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference @@ -151,6 +152,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac @Inject lateinit var userWalletsListManager: UserWalletsListManager + @Inject + lateinit var emailSender: EmailSender + internal val viewModel: MainViewModel by viewModels() private lateinit var appThemeModeFlow: SharedFlow @@ -234,6 +238,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac cardSdkConfigRepository = cardSdkConfigRepository, sendRouter = sendRouter, qrScanningRouter = qrScanningRouter, + emailSender = emailSender, ), ) } diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index 80c118ae61..d731d06dd8 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -32,15 +32,17 @@ import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.common.LogConfig import com.tangem.domain.feedback.FeedbackManagerFeatureToggles +import com.tangem.domain.feedback.GetFeedbackEmailUseCase +import com.tangem.domain.feedback.SaveBlockchainErrorUseCase import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository -import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.repository.WalletsRepository +import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.tap.common.analytics.AnalyticsFactory @@ -78,6 +80,7 @@ internal lateinit var derivationsFinder: DerivationsFinder abstract class TangemApplication : Application(), ImageLoaderFactory { + // region DI private val entryPoint: ApplicationEntryPoint get() = EntryPoints.get(this, ApplicationEntryPoint::class.java) @@ -171,6 +174,13 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { private val blockchainSDKFactory: BlockchainSDKFactory get() = entryPoint.getBlockchainSDKFactory() + private val getFeedbackEmailUseCase: GetFeedbackEmailUseCase + get() = entryPoint.getGetFeedbackEmailUseCase() + + private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase + get() = entryPoint.getSaveBlockchainErrorUseCase() + // endregion + override fun onCreate() { super.onCreate() @@ -253,6 +263,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { tangemSdkLogger = tangemSdkLogger, settingsRepository = settingsRepository, blockchainSDKFactory = blockchainSDKFactory, + saveBlockchainErrorUseCase = saveBlockchainErrorUseCase, ), ), ) @@ -344,6 +355,8 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { infoHolder = additionalFeedbackInfo, logCollector = tangemLogCollector, chatManager = ChatManager(foregroundActivityObserver), + feedbackManagerFeatureToggles = feedbackManagerFeatureToggles, + getFeedbackEmailUseCase = getFeedbackEmailUseCase, ) store.dispatch(GlobalAction.SetFeedbackManager(feedbackManager)) } diff --git a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt index 83a14893d8..2f7a4ac1cc 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt @@ -3,6 +3,8 @@ package com.tangem.tap.common.feedback import android.content.Context import com.tangem.datasource.config.models.ChatConfig import com.tangem.domain.common.TapWorkarounds +import com.tangem.domain.feedback.FeedbackManagerFeatureToggles +import com.tangem.domain.feedback.GetFeedbackEmailUseCase import com.tangem.tap.common.chat.ChatManager import com.tangem.tap.common.extensions.sendEmail import com.tangem.tap.common.log.TangemLogCollector @@ -20,6 +22,8 @@ class FeedbackManager( val infoHolder: AdditionalFeedbackInfo, private val logCollector: TangemLogCollector, private val chatManager: ChatManager, + @Suppress("UnusedPrivateMember") private val feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles, + @Suppress("UnusedPrivateMember") private val getFeedbackEmailUseCase: GetFeedbackEmailUseCase, ) { private var sessionFeedbackFile: File? = null diff --git a/app/src/main/java/com/tangem/tap/di/domain/FeedbackDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/FeedbackDomainModule.kt index f2aea82f57..033e322536 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/FeedbackDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/FeedbackDomainModule.kt @@ -1,7 +1,8 @@ package com.tangem.tap.di.domain import android.content.Context -import com.tangem.domain.feedback.GetSupportFeedbackEmailUseCase +import com.tangem.domain.feedback.GetFeedbackEmailUseCase +import com.tangem.domain.feedback.SaveBlockchainErrorUseCase import com.tangem.domain.feedback.repository.FeedbackRepository import dagger.Module import dagger.Provides @@ -16,10 +17,16 @@ internal object FeedbackDomainModule { @Provides @Singleton - fun provideGetFeedbackToSupportUseCase( + fun provideGetFeedbackEmailUseCase( feedbackRepository: FeedbackRepository, @ApplicationContext context: Context, - ): GetSupportFeedbackEmailUseCase { - return GetSupportFeedbackEmailUseCase(feedbackRepository = feedbackRepository, resources = context.resources) + ): GetFeedbackEmailUseCase { + return GetFeedbackEmailUseCase(feedbackRepository = feedbackRepository, resources = context.resources) + } + + @Provides + @Singleton + fun provideSaveBlockchainErrorUseCase(feedbackRepository: FeedbackRepository): SaveBlockchainErrorUseCase { + return SaveBlockchainErrorUseCase(feedbackRepository = feedbackRepository) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt index 46a8f606ff..a382e52b7f 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt @@ -5,11 +5,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.email.EmailSender import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.screen.ComposeFragment -import com.tangem.domain.feedback.FeedbackManagerFeatureToggles -import com.tangem.domain.feedback.GetSupportFeedbackEmailUseCase import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.tap.common.analytics.events.Settings @@ -28,15 +25,6 @@ internal class DetailsFragment : ComposeFragment(), StoreSubscriber, private val walletsRepository: WalletsRepository, - private val feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles, - private val getSupportFeedbackEmailUseCase: GetSupportFeedbackEmailUseCase, - private val emailSender: EmailSender, private val userWalletsListManager: UserWalletsListManager, ) { @@ -149,21 +141,7 @@ internal class DetailsViewModel( private fun sendFeedback() { Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Settings)) - if (feedbackManagerFeatureToggles.isLocalLogsEnabled) { - mainScope.launch { - val email = getSupportFeedbackEmailUseCase() - emailSender.send( - email = EmailSender.Email( - address = email.address, - subject = email.subject, - message = email.message, - attachment = email.file, - ), - ) - } - } else { - store.dispatchOnMain(GlobalAction.SendEmail(FeedbackEmail())) - } + store.dispatchOnMain(GlobalAction.SendEmail(FeedbackEmail())) } private fun navigateToAppSettings() { diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt index 995658a42c..f854ba0dae 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt @@ -1,8 +1,9 @@ package com.tangem.tap.proxy.redux -import com.tangem.feature.qrscanning.QrScanningRouter +import com.tangem.core.navigation.email.EmailSender import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository +import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.features.managetokens.navigation.ManageTokensUi import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.tester.api.TesterRouter @@ -23,5 +24,6 @@ sealed interface DaggerGraphAction : Action { val cardSdkConfigRepository: CardSdkConfigRepository, val sendRouter: SendRouter, val qrScanningRouter: QrScanningRouter, + val emailSender: EmailSender, ) : DaggerGraphAction } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt index 7074523df0..b25fdce12b 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt @@ -22,6 +22,7 @@ object DaggerGraphReducer { cardSdkConfigRepository = action.cardSdkConfigRepository, sendRouter = action.sendRouter, qrScanningRouter = action.qrScanningRouter, + emailSender = action.emailSender, ) } } diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 0c09261f91..ab600ee8ea 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -2,6 +2,7 @@ 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.datasource.connection.NetworkConnectionManager import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.apptheme.repository.AppThemeModeRepository @@ -11,15 +12,16 @@ import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.feedback.FeedbackManagerFeatureToggles +import com.tangem.domain.feedback.SaveBlockchainErrorUseCase import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository -import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.repository.WalletsRepository +import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles import com.tangem.features.managetokens.navigation.ManageTokensUi @@ -69,4 +71,6 @@ data class DaggerGraphState( val tangemSdkLogger: TangemSdkLogger? = null, val settingsRepository: SettingsRepository? = null, val blockchainSDKFactory: BlockchainSDKFactory? = null, + val emailSender: EmailSender? = null, + val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase? = null, ) : StateType \ No newline at end of file diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/GetFeedbackEmailUseCase.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/GetFeedbackEmailUseCase.kt new file mode 100644 index 0000000000..50998a4ed8 --- /dev/null +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/GetFeedbackEmailUseCase.kt @@ -0,0 +1,67 @@ +package com.tangem.domain.feedback + +import android.content.res.Resources +import com.tangem.domain.feedback.models.CardInfo +import com.tangem.domain.feedback.models.FeedbackEmail +import com.tangem.domain.feedback.models.FeedbackEmailType +import com.tangem.domain.feedback.repository.FeedbackRepository +import com.tangem.domain.feedback.utils.* + +/** + * Get email with feedback for support + * + * @property feedbackRepository feedback repository + * @property resources resources for getting strings + * +[REDACTED_AUTHOR] + */ +class GetFeedbackEmailUseCase( + private val feedbackRepository: FeedbackRepository, + private val resources: Resources, +) { + + private val emailSubjectResolver = EmailSubjectResolver(resources) + private val emailMessageTitleResolver = EmailMessageTitleResolver(resources) + private val emailMessageBodyResolver = EmailMessageBodyResolver(feedbackRepository) + + suspend operator fun invoke(type: FeedbackEmailType): FeedbackEmail { + val cardInfo = feedbackRepository.getCardInfo() + + val formattedLogs = AppLogsFormatter().format(appLogs = feedbackRepository.getAppLogs()) + + return FeedbackEmail( + address = getAddress(cardInfo), + subject = emailSubjectResolver.resolve(type, cardInfo), + message = createMessage(type, cardInfo), + file = feedbackRepository.createLogFile(logs = formattedLogs), + ) + } + + private fun getAddress(cardInfo: CardInfo): String { + return if (cardInfo.isStart2Coin) START2COIN_SUPPORT_EMAIL else TANGEM_SUPPORT_EMAIL + } + + private suspend fun createMessage(type: FeedbackEmailType, cardInfo: CardInfo): String { + return StringBuilder().apply { + val title = emailMessageTitleResolver.resolve(type) + append(title) + + skipLine() + + appendDisclaimerIfNeeded(type) + + skipLine() + + val body = emailMessageBodyResolver.resolve(type, cardInfo) + append(body) + }.toString() + } + + private fun StringBuilder.appendDisclaimerIfNeeded(type: FeedbackEmailType): StringBuilder { + return if (type is FeedbackEmailType.ScanningProblem) { + this + } else { + append(resources.getString(R.string.feedback_data_collection_message)) + } + } +} \ No newline at end of file diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/GetSupportFeedbackEmailUseCase.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/GetSupportFeedbackEmailUseCase.kt deleted file mode 100644 index 6ab491c734..0000000000 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/GetSupportFeedbackEmailUseCase.kt +++ /dev/null @@ -1,113 +0,0 @@ -package com.tangem.domain.feedback - -import android.content.res.Resources -import com.tangem.domain.feedback.models.CardInfo -import com.tangem.domain.feedback.models.SupportFeedbackEmail -import com.tangem.domain.feedback.repository.FeedbackRepository -import com.tangem.domain.feedback.utils.skipLine -import org.joda.time.DateTime -import org.joda.time.format.DateTimeFormatterBuilder -import java.util.Locale - -/** - * Get email with feedback for support - * - * @property feedbackRepository feedback repository - * @property resources resources for getting strings - * -[REDACTED_AUTHOR] - */ -class GetSupportFeedbackEmailUseCase( - private val feedbackRepository: FeedbackRepository, - private val resources: Resources, -) { - - // 00.00 00:00:00.000 - private val dateFormatter = DateTimeFormatterBuilder() - .appendDayOfMonth(2) - .appendLiteral('.') - .appendMonthOfYear(2) - .appendLiteral(' ') - .appendHourOfDay(2) - .appendLiteral(':') - .appendMinuteOfHour(2) - .appendLiteral(':') - .appendSecondOfMinute(2) - .appendLiteral('.') - .appendMillisOfSecond(3) - .toFormatter() - .withLocale(Locale.getDefault()) - - suspend operator fun invoke(): SupportFeedbackEmail { - val cardInfo = feedbackRepository.getCardInfo() - - return SupportFeedbackEmail( - address = getEmail(isStart2Coin = cardInfo.isStart2Coin), - subject = getSubject(isStart2Coin = cardInfo.isStart2Coin), - message = createMessage(cardInfo), - file = feedbackRepository.createLogFile(logs = getLogs()), - ) - } - - private fun getEmail(isStart2Coin: Boolean): String { - return if (isStart2Coin) START2COIN_SUPPORT_EMAIL else TANGEM_SUPPORT_EMAIL - } - - private fun getSubject(isStart2Coin: Boolean): String { - return resources.getString( - if (isStart2Coin) { - R.string.feedback_subject_support - } else { - R.string.feedback_subject_support_tangem - }, - ) - } - - private suspend fun createMessage(cardInfo: CardInfo): String { - return StringBuilder().apply { - append(resources.getString(R.string.feedback_preface_support)) - skipLine() - append(resources.getString(R.string.feedback_data_collection_message)) - skipLine() - append( - FeedbackDataBuilder().apply { - addUserWalletsInfo(userWalletsInfo = feedbackRepository.getUserWalletsInfo()) - addDelimiter() - addCardInfo(cardInfo) - addDelimiter() - addBlockchainInfoList(blockchainInfoList = feedbackRepository.getBlockchainInfoList()) - addDelimiter() - addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo()) - }.build(), - ) - }.toString() - } - - private suspend fun getLogs(): String { - val builder = StringBuilder() - - var sum = 0 - val appLogs = feedbackRepository.getAppLogs() - for (i in appLogs.lastIndex downTo 0) { - val log = appLogs[i] - val date = dateFormatter.print(DateTime(log.timestamp)) - - val formattedLog = "$date: ${log.message}\n" - - sum += formattedLog.length - if (sum < GMAIL_MAX_FILE_SIZE) { - builder.insert(0, formattedLog) - } else { - break - } - } - - return builder.toString() - } - - private companion object { - const val START2COIN_SUPPORT_EMAIL = "cardsupport@start2coin.com" - const val TANGEM_SUPPORT_EMAIL = "support@tangem.com" - const val GMAIL_MAX_FILE_SIZE = 24_900_000 // ≈ 25 MB - } -} \ No newline at end of file diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/models/SupportFeedbackEmail.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmail.kt similarity index 83% rename from domain/feedback/src/main/java/com/tangem/domain/feedback/models/SupportFeedbackEmail.kt rename to domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmail.kt index 35faf18f6f..9187f590f0 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/models/SupportFeedbackEmail.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmail.kt @@ -2,7 +2,7 @@ package com.tangem.domain.feedback.models import java.io.File -data class SupportFeedbackEmail( +data class FeedbackEmail( val address: String, val subject: String, val message: String,