diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index a663e66e7a..7593f303e7 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -27,11 +27,11 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.shop.redux.ShopAction import com.tangem.wallet.R import com.tangem.wallet.databinding.ActivityMainBinding +import java.lang.ref.WeakReference +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import java.lang.ref.WeakReference -import kotlin.coroutines.CoroutineContext lateinit var tangemSdk: TangemSdk lateinit var tangemSdkManager: TangemSdkManager @@ -57,7 +57,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) systemActions() - store.state.globalState.feedbackManager?.updateActivity(this) store.dispatch(NavigationAction.ActivityCreated(WeakReference(this))) tangemSdk = TangemSdk.init(this, TangemSdkManager.config) diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 61b576837d..518ad410c4 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -89,9 +89,16 @@ class TapApplication : Application(), ImageLoaderFactory { val localLoader = FeaturesLocalLoader(this, moshi) val remoteLoader = FeaturesRemoteLoader(moshi) val configManager = ConfigManager(localLoader, remoteLoader) - configManager.load { + configManager.load { config -> store.dispatch(GlobalAction.SetConfigManager(configManager)) - shopService = TangemShopService(this, configManager.config.shopify!!) + shopService = TangemShopService( + application = this, + shopifyShop = config.shopify!! + ) + store.state.globalState.feedbackManager?.initChat( + context = this, + zendeskConfig = config.zendesk!! + ) } val warningsManager = WarningMessagesManager(RemoteWarningLoader(moshi)) warningsManager.load { store.dispatch(GlobalAction.SetWarningManager(warningsManager)) } 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 3a72ece232..dccb805723 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 @@ -1,14 +1,22 @@ package com.tangem.tap.common.feedback -import android.app.Activity +import android.content.Context import com.tangem.domain.common.TapWorkarounds import com.tangem.tap.common.extensions.sendEmail import com.tangem.tap.common.log.TangemLogCollector +import com.tangem.tap.common.zendesk.ZendeskConfig +import com.tangem.tap.foregroundActivityObserver +import com.tangem.tap.withForegroundActivity import java.io.File import java.io.FileWriter import java.io.StringWriter import timber.log.Timber - +import zendesk.configurations.Configuration +import zendesk.core.Zendesk +import zendesk.support.Support +import zendesk.support.request.RequestConfiguration +import zendesk.support.requestlist.RequestListActivity +import zendesk.support.requestlist.RequestListConfiguration /** [REDACTED_AUTHOR] @@ -17,38 +25,47 @@ class FeedbackManager( val infoHolder: AdditionalFeedbackInfo, private val logCollector: TangemLogCollector, ) { - - private lateinit var activity: Activity - - fun updateActivity(activity: Activity) { - this.activity = activity + fun initChat( + context: Context, + zendeskConfig: ZendeskConfig, + ) { + Zendesk.INSTANCE.init( + /* context = */ context, + /* zendeskUrl = */ zendeskConfig.url, + /* applicationId = */ zendeskConfig.appId, + /* oauthClientId = */ zendeskConfig.clientId, + ) + Support.INSTANCE.init(Zendesk.INSTANCE) } fun sendEmail(feedbackData: FeedbackData, onFail: ((Exception) -> Unit)? = null) { - if (!this::activity.isInitialized) return - feedbackData.prepare(infoHolder) - val fileLog = if (feedbackData is ScanFailsEmail) createLogFile() else null - activity.sendEmail( - email = getSupportEmail(), - subject = activity.getString(feedbackData.subjectResId), - message = feedbackData.joinTogether(activity, infoHolder), - file = fileLog, - onFail = onFail - ) - } - - private fun getSupportEmail(): String { - return if (TapWorkarounds.isStart2CoinIssuer(infoHolder.cardIssuer)) { - S2C_SUPPORT_EMAIL - } else { - DEFAULT_SUPPORT_EMAIL + foregroundActivityObserver.withForegroundActivity { activity -> + val fileLog = if (feedbackData is ScanFailsEmail) createLogFile(activity) else null + activity.sendEmail( + email = getSupportEmail(), + subject = activity.getString(feedbackData.subjectResId), + message = feedbackData.joinTogether(activity, infoHolder), + file = fileLog, + onFail = onFail + ) } } - private fun createLogFile(): File? { + fun openChat(feedbackData: FeedbackData) { + feedbackData.prepare(infoHolder) + foregroundActivityObserver.withForegroundActivity { activity -> + RequestListActivity.builder() + .show( + /* context = */ activity, + /* configurations = */ buildConfigs(activity, feedbackData) + ) + } + } + + private fun createLogFile(context: Context): File? { return try { - val file = File(activity.filesDir, "logs.txt") + val file = File(context.filesDir, "logs.txt") file.delete() file.createNewFile() @@ -60,11 +77,35 @@ class FeedbackManager( logCollector.clearLogs() file } catch (ex: Exception) { - Timber.e(ex, "Can't create a file for email attachment") + Timber.e(ex, "Can't create the logs file") null } } + private fun buildConfigs( + context: Context, + feedbackData: FeedbackData, + ): List { + return listOf( + // Request configuration + RequestConfiguration.Builder() + .withRequestSubject(context.getString(feedbackData.subjectResId)) + .config(), + // Request list configuration + RequestListConfiguration.Builder() + .withContactUsButtonVisible(true) + .config(), + ) + } + + private fun getSupportEmail(): String { + return if (TapWorkarounds.isStart2CoinIssuer(infoHolder.cardIssuer)) { + S2C_SUPPORT_EMAIL + } else { + DEFAULT_SUPPORT_EMAIL + } + } + companion object { const val DEFAULT_SUPPORT_EMAIL = "support@tangem.com" const val S2C_SUPPORT_EMAIL = "cardsupport@start2coin.com" diff --git a/app/src/main/java/com/tangem/tap/common/zendesk/ZendeskConfig.kt b/app/src/main/java/com/tangem/tap/common/zendesk/ZendeskConfig.kt new file mode 100644 index 0000000000..aad872b7df --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/zendesk/ZendeskConfig.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.common.zendesk + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class ZendeskConfig( + @Json(name = "zendeskApiKey") + val apiKey: String, + @Json(name = "zendeskAppId") + val appId: String, + @Json(name = "zendeskClientId") + val clientId: String, + @Json(name = "zendeskUrl") + val url: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt index 52037f828c..795e76fe06 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt @@ -1,8 +1,8 @@ package com.tangem.tap.domain.configurable.config import com.tangem.blockchain.common.BlockchainSdkConfig -import com.tangem.common.extensions.VoidCallback import com.tangem.tap.common.shop.shopify.ShopifyShop +import com.tangem.tap.common.zendesk.ZendeskConfig import com.tangem.tap.domain.configurable.Loader /** @@ -20,7 +20,8 @@ data class Config( val isTopUpEnabled: Boolean = false, @Deprecated("Not relevant since version 3.23") val isCreatingTwinCardsAllowed: Boolean = false, - val shopify: ShopifyShop? = null + val shopify: ShopifyShop? = null, + val zendesk: ZendeskConfig? = null, ) class ConfigManager( @@ -33,11 +34,11 @@ class ConfigManager( private var defaultConfig = Config() - fun load(onComplete: VoidCallback? = null) { - localLoader.load { config -> - setupFeature(config.features) - setupKey(config.configValues) - onComplete?.invoke() + fun load(onComplete: ((config: Config) -> Unit)? = null) { + localLoader.load { configModel -> + setupFeature(configModel.features) + setupKey(configModel.configValues) + onComplete?.invoke(config) } // Uncomment to enable remote config // remoteLoader.load { config -> @@ -94,6 +95,7 @@ class ConfigManager( ), appsFlyerDevKey = values.appsFlyerDevKey, shopify = values.shopifyShop, + zendesk = values.zendesk, ) defaultConfig = defaultConfig.copy( coinMarketCapKey = values.coinMarketCapKey, @@ -109,6 +111,7 @@ class ConfigManager( ), appsFlyerDevKey = values.appsFlyerDevKey, shopify = values.shopifyShop, + zendesk = values.zendesk, ) } diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt index f9e053ee98..fc63ed5e9a 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt @@ -1,6 +1,7 @@ package com.tangem.tap.domain.configurable.config import com.tangem.tap.common.shop.shopify.ShopifyShop +import com.tangem.tap.common.zendesk.ZendeskConfig /** [REDACTED_AUTHOR] @@ -24,7 +25,8 @@ class ConfigValueModel( val blockcypherTokens: Set?, val infuraProjectId: String?, val appsFlyerDevKey: String, - val shopifyShop: ShopifyShop? + val shopifyShop: ShopifyShop?, + val zendesk: ZendeskConfig?, ) class ConfigModel(val features: FeatureModel?, val configValues: ConfigValueModel?) {