Updated on 2026-08-14

This commit is contained in:
Tangem 2023-07-24 13:45:56 +03:00
commit ea661f55ab
250 changed files with 6169 additions and 949 deletions

View file

@ -1,8 +1,10 @@
package com.tangem.datasource.api.common
import android.util.Log
import com.ihsanbal.logging.Level
import com.ihsanbal.logging.LoggingInterceptor
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit
@ -22,7 +24,7 @@ fun createRetrofitInstance(
}
interceptors.forEach { okHttpBuilder.addInterceptor(it) }
if (logEnabled) okHttpBuilder.addInterceptor(createHttpLoggingInterceptor())
if (logEnabled) okHttpBuilder.addInterceptor(createNetworkLoggingInterceptor())
return Retrofit.Builder()
.baseUrl(baseUrl)
@ -31,6 +33,9 @@ fun createRetrofitInstance(
.build()
}
private fun createHttpLoggingInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
fun createNetworkLoggingInterceptor(): Interceptor {
return LoggingInterceptor.Builder()
.setLevel(Level.BODY)
.log(Log.VERBOSE)
.build()
}

View file

@ -0,0 +1,20 @@
package com.tangem.datasource.di
import com.tangem.datasource.local.cache.CacheKeysStore
import com.tangem.datasource.local.cache.RuntimeCacheKeysStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object CacheKeysStoreModule {
@Provides
@Singleton
fun provideCacheKeysStore(): CacheKeysStore {
return RuntimeCacheKeysStore()
}
}

View file

@ -63,9 +63,7 @@ class NetworkModule {
@PromotionOneInch
fun providePromotionOneInchApi(authProvider: AuthProvider, @NetworkMoshi moshi: Moshi): PromotionApi {
val okClient = OkHttpClient.Builder()
.addHeaders(
AuthenticationHeader(authProvider),
)
.addHeaders(AuthenticationHeader(authProvider))
.allowLogging()
.callTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.connectTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)

View file

@ -0,0 +1,14 @@
package com.tangem.datasource.local.cache
import com.tangem.datasource.local.cache.model.CacheKey
interface CacheKeysStore {
fun get(id: String): CacheKey?
fun addOrReplace(key: CacheKey)
fun remove(id: String)
fun clear()
}

View file

@ -0,0 +1,25 @@
package com.tangem.datasource.local.cache
import com.tangem.datasource.local.cache.model.CacheKey
import com.tangem.datasource.local.store.RuntimeStore
internal class RuntimeCacheKeysStore : CacheKeysStore {
private val store = RuntimeStore(keyProvider = CacheKey::id)
override fun get(id: String): CacheKey? {
return store.getSync { it.id == id }.firstOrNull()
}
override fun addOrReplace(key: CacheKey) {
store.addOrReplace(key)
}
override fun remove(id: String) {
store.remove { it.id == id }
}
override fun clear() {
store.clear()
}
}

View file

@ -0,0 +1,10 @@
package com.tangem.datasource.local.cache.model
import org.joda.time.Duration
import org.joda.time.LocalDateTime
data class CacheKey(
val id: String,
val updatedAt: LocalDateTime,
val expiresIn: Duration,
)

View file

@ -0,0 +1,59 @@
package com.tangem.datasource.local.store
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
internal class RuntimeStore<Key, Data>(private val keyProvider: (Data) -> Key) {
private val store = MutableStateFlow<HashMap<Key, Data>>(hashMapOf())
fun get(selector: (Data) -> Boolean = { true }): Flow<List<Data>> {
return store.map { getInternal(it, selector) }
}
fun getSync(selector: (Data) -> Boolean = { true }): List<Data> {
return getInternal(store.value, selector)
}
// TODO: Uncomment if needed
// fun addOrReplace(items: Collection<Data>) {
// if (items.isEmpty()) return
//
// val storeValue = store.value
//
// items.forEach { item ->
// storeValue[keyProvider(item)] = item
// }
//
// store.value = storeValue
// }
fun addOrReplace(item: Data) {
val storeValue = store.value
storeValue[keyProvider(item)] = item
store.value = storeValue
}
fun remove(selector: (Data) -> Boolean) {
val storeValue = store.value
storeValue.forEach { (key, item) ->
if (selector(item)) {
storeValue.remove(key)
}
}
store.value = storeValue
}
fun clear() {
store.update { hashMapOf() }
}
private fun getInternal(store: HashMap<Key, Data>, selector: (Data) -> Boolean): List<Data> {
return store.values.filter { selector(it) }
}
}

View file

@ -1,10 +1,9 @@
package com.tangem.datasource.utils
import com.tangem.datasource.BuildConfig
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.logging.HttpLoggingInterceptor.Level
/** Extension for adding headers [requestHeaders] to every [OkHttpClient] request */
internal fun OkHttpClient.Builder.addHeaders(vararg requestHeaders: RequestHeader): OkHttpClient.Builder {
@ -26,5 +25,10 @@ internal fun OkHttpClient.Builder.addHeaders(vararg requestHeaders: RequestHeade
*
* @param level logging level. By default, only the request body.
*/
internal fun OkHttpClient.Builder.allowLogging(level: Level = Level.BODY): OkHttpClient.Builder =
if (BuildConfig.DEBUG) addInterceptor(interceptor = HttpLoggingInterceptor().setLevel(level)) else this
internal fun OkHttpClient.Builder.allowLogging(): OkHttpClient.Builder {
return if (BuildConfig.DEBUG) {
addInterceptor(interceptor = createNetworkLoggingInterceptor())
} else {
this
}
}