Updated on 2026-08-14

This commit is contained in:
Tangem 2023-10-18 09:53:47 +03:00
parent 38ab05aeed
commit 7d6d4fddf6
13 changed files with 82 additions and 110 deletions

View file

@ -172,4 +172,10 @@ dependencies {
testImplementation(deps.test.truth)
androidTestImplementation(deps.test.junit.android)
androidTestImplementation(deps.test.espresso)
/** Chucker */
debugImplementation(deps.chucker)
externalImplementation(deps.chuckerStub)
internalImplementation(deps.chuckerStub)
releaseImplementation(deps.chuckerStub)
}

View file

@ -12,6 +12,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

View file

@ -1,14 +1,19 @@
package com.tangem.tap
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
@ -52,6 +57,7 @@ import com.tangem.tap.features.welcome.ui.WelcomeFragment
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.proxy.redux.DaggerGraphAction
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
import com.tangem.wallet.databinding.ActivityMainBinding
import dagger.hilt.android.AndroidEntryPoint
@ -139,6 +145,8 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
initContent()
checkGooglePayAvailability()
checkForNotificationPermission()
}
private fun installActivityDependencies() {
@ -371,4 +379,14 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
store.dispatch(BackupAction.CheckForUnfinishedBackup)
}
private fun checkForNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
BuildConfig.DEBUG &&
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) !=
PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0)
}
}
}

View file

@ -5,6 +5,7 @@ import android.content.Context
import android.content.pm.PackageManager
import coil.ImageLoader
import coil.ImageLoaderFactory
import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.Logger
import com.tangem.Log
@ -259,6 +260,7 @@ internal class TapApplication : Application(), ImageLoaderFactory {
if (LogConfig.network.blockchainSdkNetwork) {
BlockchainSdkRetrofitBuilder.interceptors = listOf(
createNetworkLoggingInterceptor(),
ChuckerInterceptor(this),
)
}

View file

@ -47,4 +47,11 @@ dependencies {
/** Security */
implementation(deps.spongecastle.core)
/** Chucker */
/** Chucker */
debugImplementation(deps.chucker)
externalImplementation(deps.chuckerStub)
internalImplementation(deps.chuckerStub)
releaseImplementation(deps.chuckerStub)
}

View file

@ -1,38 +0,0 @@
package com.tangem.datasource.api.paymentology
import com.tangem.datasource.api.paymentology.models.request.CheckRegistrationRequests
import com.tangem.datasource.api.paymentology.models.request.RegisterKYCRequest
import com.tangem.datasource.api.paymentology.models.request.RegisterWalletRequest
import com.tangem.datasource.api.paymentology.models.response.AttestationResponse
import com.tangem.datasource.api.paymentology.models.response.RegisterWalletResponse
import com.tangem.datasource.api.paymentology.models.response.RegistrationResponse
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST
/**
* Interface of Paymentology Api.
*
* IMPORTANT: Cannot replace [Headers] annotations with OkHttpClient.addHeader(), because OkHttp adds encoding to
* content type value. But Paymentology API expects a value without the charset.
*
[REDACTED_AUTHOR]
*/
interface PaymentologyApi {
@Headers("Content-Type: application/json")
@POST("card/verify")
suspend fun checkRegistration(@Body request: CheckRegistrationRequests): RegistrationResponse
@Headers("Content-Type: application/json")
@POST("card/get_challenge")
suspend fun requestAttestationChallenge(@Body request: CheckRegistrationRequests.Item): AttestationResponse
@Headers("Content-Type: application/json")
@POST("card/set_pin")
suspend fun registerWallet(@Body request: RegisterWalletRequest): RegisterWalletResponse
@Headers("Content-Type: application/json")
@POST("card/kyc")
suspend fun registerKYC(@Body request: RegisterKYCRequest): RegisterWalletResponse
}

View file

@ -1,30 +0,0 @@
package com.tangem.datasource.api.paymentology
import com.tangem.datasource.api.common.MoshiConverter
import com.tangem.datasource.utils.allowLogging
import okhttp3.OkHttpClient
import retrofit2.Retrofit
/**
[REDACTED_AUTHOR]
*/
// TODO("Remove after removing Redux")
@Deprecated("Use PaymentologyApi")
object PaymentologyApiService {
val api = createApi()
private const val PAYMENTOLOGY_BASE_URL: String = "https://paymentologygate.oa.r.appspot.com/"
private fun createApi(): PaymentologyApi {
return Retrofit.Builder()
.addConverterFactory(MoshiConverter.networkMoshiConverter)
.baseUrl(PAYMENTOLOGY_BASE_URL)
.client(
OkHttpClient.Builder()
.allowLogging()
.build(),
)
.build()
.create(PaymentologyApi::class.java)
}
}

View file

@ -6,7 +6,7 @@ import com.tangem.datasource.utils.RequestHeader
import com.tangem.datasource.utils.RequestHeader.AuthenticationHeader
import com.tangem.datasource.utils.RequestHeader.CacheControlHeader
import com.tangem.datasource.utils.addHeaders
import com.tangem.datasource.utils.allowLogging
import com.tangem.datasource.utils.addLoggers
import okhttp3.OkHttpClient
import retrofit2.Retrofit
@ -35,7 +35,7 @@ object TangemTechService {
.client(
OkHttpClient.Builder()
.addHeaders(*headers.toTypedArray())
.allowLogging()
.addLoggers()
.build(),
)
.build()

View file

@ -1,17 +1,18 @@
package com.tangem.datasource.di
import android.content.Context
import com.squareup.moshi.Moshi
import com.tangem.datasource.api.common.response.ApiResponseCallAdapterFactory
import com.tangem.datasource.api.paymentology.PaymentologyApi
import com.tangem.datasource.api.promotion.PromotionApi
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.utils.RequestHeader.*
import com.tangem.datasource.utils.addHeaders
import com.tangem.datasource.utils.allowLogging
import com.tangem.datasource.utils.addLoggers
import com.tangem.lib.auth.AuthProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import retrofit2.Retrofit
@ -25,7 +26,7 @@ class NetworkModule {
@Provides
@Singleton
fun provideTangemTechApi(@NetworkMoshi moshi: Moshi): TangemTechApi {
fun provideTangemTechApi(@NetworkMoshi moshi: Moshi, @ApplicationContext context: Context): TangemTechApi {
return Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(ApiResponseCallAdapterFactory.create())
@ -37,35 +38,24 @@ class NetworkModule {
// TODO("refactor header init") get auth data after biometric auth to avoid race condition
// AuthenticationHeader(authProvider),
)
.allowLogging()
.addLoggers(context)
.build(),
)
.build()
.create(TangemTechApi::class.java)
}
@Provides
@Singleton
fun providePaymentologyApi(@NetworkMoshi moshi: Moshi): PaymentologyApi {
return Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(PAYMENTOLOGY_BASE_URL)
.client(
OkHttpClient.Builder()
.allowLogging()
.build(),
)
.build()
.create(PaymentologyApi::class.java)
}
@Provides
@Singleton
@PromotionOneInch
fun providePromotionOneInchApi(authProvider: AuthProvider, @NetworkMoshi moshi: Moshi): PromotionApi {
fun providePromotionOneInchApi(
authProvider: AuthProvider,
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
): PromotionApi {
val okClient = OkHttpClient.Builder()
.addHeaders(AuthenticationHeader(authProvider))
.allowLogging()
.addLoggers(context)
.callTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.connectTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.readTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)

View file

@ -1,12 +1,14 @@
package com.tangem.datasource.di
import android.content.Context
import com.squareup.moshi.Moshi
import com.tangem.datasource.api.oneinch.OneInchApi
import com.tangem.datasource.api.oneinch.OneInchApiFactory
import com.tangem.datasource.utils.allowLogging
import com.tangem.datasource.utils.addLoggers
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import retrofit2.Retrofit
@ -19,23 +21,31 @@ class OneInchApisModule {
@Provides
@Singleton
fun provideOneInchApiFactory(@NetworkMoshi moshi: Moshi): OneInchApiFactory {
val apiFactory = OneInchApiFactory()
apiFactory.putApi(ETH_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_ETH_PATH, moshi))
apiFactory.putApi(BSC_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_BSC_PATH, moshi))
apiFactory.putApi(POLYGON_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_POLYGON_PATH, moshi))
apiFactory.putApi(OPTIMISM_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_OPTIMISM_PATH, moshi))
apiFactory.putApi(ARBITRUM_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_ARBITRUM_PATH, moshi))
apiFactory.putApi(GNOSIS_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_GNOSIS_PATH, moshi))
apiFactory.putApi(
AVALANCHE_NETWORK,
createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_AVALANCHE_PATH, moshi),
fun provideOneInchApiFactory(@NetworkMoshi moshi: Moshi, @ApplicationContext context: Context): OneInchApiFactory {
val networks = mapOf(
ETH_NETWORK to ONE_INCH_ETH_PATH,
BSC_NETWORK to ONE_INCH_BSC_PATH,
POLYGON_NETWORK to ONE_INCH_POLYGON_PATH,
OPTIMISM_NETWORK to ONE_INCH_OPTIMISM_PATH,
ARBITRUM_NETWORK to ONE_INCH_ARBITRUM_PATH,
GNOSIS_NETWORK to ONE_INCH_GNOSIS_PATH,
AVALANCHE_NETWORK to ONE_INCH_AVALANCHE_PATH,
FANTOM_NETWORK to ONE_INCH_FANTOM_PATH,
)
apiFactory.putApi(FANTOM_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_FANTOM_PATH, moshi))
val apiFactory = OneInchApiFactory()
for ((network, path) in networks) {
apiFactory.putApi(
networkId = network,
api = createOneInchApiWithUrl("$ONE_INCH_BASE_URL$path", moshi, context),
)
}
return apiFactory
}
private fun createOneInchApiWithUrl(url: String, moshi: Moshi): OneInchApi {
private fun createOneInchApiWithUrl(url: String, moshi: Moshi, context: Context): OneInchApi {
return Retrofit.Builder()
.addConverterFactory(
MoshiConverterFactory.create(moshi),
@ -43,7 +53,7 @@ class OneInchApisModule {
.baseUrl(url)
.client(
OkHttpClient.Builder()
.allowLogging()
.addLoggers(context)
.build(),
)
.build()

View file

@ -1,5 +1,7 @@
package com.tangem.datasource.utils
import android.content.Context
import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.tangem.datasource.BuildConfig
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
import okhttp3.Interceptor
@ -25,8 +27,11 @@ internal fun OkHttpClient.Builder.addHeaders(vararg requestHeaders: RequestHeade
*
* @param level logging level. By default, only the request body.
*/
internal fun OkHttpClient.Builder.allowLogging(): OkHttpClient.Builder {
return if (BuildConfig.DEBUG) {
internal fun OkHttpClient.Builder.addLoggers(context: Context? = null): OkHttpClient.Builder {
return if (BuildConfig.LOG_ENABLED) {
context?.let {
addInterceptor(interceptor = ChuckerInterceptor(it))
}
addInterceptor(interceptor = createNetworkLoggingInterceptor())
} else {
this

View file

@ -1,6 +1,5 @@
package com.tangem.domain.redux.global
import com.tangem.datasource.api.paymentology.PaymentologyApiService
import com.tangem.datasource.api.tangemTech.TangemTechService
import com.tangem.domain.models.scan.ScanResponse
@ -17,5 +16,4 @@ data class DomainGlobalState(
data class NetworkServices(
val tangemTechService: TangemTechService = TangemTechService,
val paymentologyService: PaymentologyApiService = PaymentologyApiService,
)

View file

@ -79,6 +79,7 @@ walletConnectCore = "1.18.0"
walletConnectWeb3 = "1.11.0"
prettyLogger = "2.2.0"
okHttp-prettyLogging = "3.1.0"
chucker = "4.0.0"
# endregion Other libraries
# region Tangem
@ -230,4 +231,6 @@ reactive-network = { module = "com.github.pwittchen:reactivenetwork-rx2", versio
walletConnectCore = { module = "com.walletconnect:android-core", version.ref = "walletConnectCore" }
walletConnectWeb3 = { module = "com.walletconnect:web3wallet", version.ref = "walletConnectWeb3" }
prettyLogger = { module = "com.orhanobut:logger", version.ref = "prettyLogger" }
chucker = { module = "com.github.chuckerteam.chucker:library", version.ref = "chucker" }
chuckerStub = { module = "com.github.chuckerteam.chucker:library-no-op", version.ref = "chucker" }
# endregion Other