Updated on 2026-08-14
This commit is contained in:
commit
da717845f0
538 changed files with 7350 additions and 5597 deletions
|
|
@ -5,6 +5,7 @@ plugins {
|
|||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.hilt.android)
|
||||
alias(deps.plugins.ksp)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +25,7 @@ dependencies {
|
|||
implementation(deps.moshi)
|
||||
implementation(deps.moshi.kotlin)
|
||||
implementation(deps.timber)
|
||||
kaptForObfuscatingVariants(deps.moshi.kotlin.codegen)
|
||||
ksp(deps.moshi.kotlin.codegen)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.datasource)
|
||||
|
|
|
|||
|
|
@ -26,5 +26,13 @@
|
|||
{
|
||||
"name": "alephium",
|
||||
"version": "5.21.0"
|
||||
},
|
||||
{
|
||||
"name": "scroll",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "zklink",
|
||||
"version": "undefined"
|
||||
}
|
||||
]
|
||||
|
|
@ -46,5 +46,9 @@
|
|||
{
|
||||
"name": "BALANCES_CACHING_ENABLED",
|
||||
"version": "5.21.0"
|
||||
},
|
||||
{
|
||||
"name": "NFT_ENABLED",
|
||||
"version": "undefined"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ plugins {
|
|||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.hilt.android)
|
||||
alias(deps.plugins.room)
|
||||
alias(deps.plugins.ksp)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ dependencies {
|
|||
implementation(deps.okHttp.prettyLogging)
|
||||
implementation(deps.retrofit)
|
||||
implementation(deps.retrofit.moshi)
|
||||
kaptForObfuscatingVariants(deps.moshi.kotlin.codegen)
|
||||
ksp(deps.moshi.kotlin.codegen)
|
||||
kaptForObfuscatingVariants(deps.retrofit.response.type.keeper)
|
||||
|
||||
/** Time */
|
||||
|
|
@ -78,7 +79,7 @@ dependencies {
|
|||
implementation(deps.androidx.datastore)
|
||||
implementation(deps.room.runtime)
|
||||
implementation(deps.room.ktx)
|
||||
kapt(deps.room.compiler)
|
||||
ksp(deps.room.compiler)
|
||||
|
||||
testImplementation(deps.test.coroutine)
|
||||
testImplementation(deps.test.junit)
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
package com.tangem.datasource.api.common.visa
|
||||
|
||||
interface TangemVisaAuthProvider {
|
||||
|
||||
suspend fun getAuthHeader(cardId: String): String
|
||||
}
|
||||
|
|
@ -5,9 +5,7 @@ import com.tangem.datasource.api.utils.ReadTimeout
|
|||
import com.tangem.datasource.api.visa.models.request.ActivationByCardWalletRequest
|
||||
import com.tangem.datasource.api.visa.models.request.ActivationByCustomerWalletRequest
|
||||
import com.tangem.datasource.api.visa.models.request.SetPinCodeRequest
|
||||
import com.tangem.datasource.api.visa.models.response.CardActivationRemoteStateResponse
|
||||
import com.tangem.datasource.api.visa.models.response.CardWalletDataToSignResponse
|
||||
import com.tangem.datasource.api.visa.models.response.CustomerWalletDataToSignResponse
|
||||
import com.tangem.datasource.api.visa.models.response.*
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
|
|
@ -71,4 +69,19 @@ interface TangemVisaApi {
|
|||
@Header("Authorization") authHeader: String,
|
||||
@Body body: SetPinCodeRequest,
|
||||
): ApiResponse<Unit>
|
||||
|
||||
@GET("customer/info")
|
||||
suspend fun getCustomerInfo(
|
||||
@Header("Authorization") authHeader: String,
|
||||
@Query("card_id") cardId: String,
|
||||
): ApiResponse<VisaCustomerInfo>
|
||||
|
||||
@GET("product_instance/transactions")
|
||||
suspend fun getTxHistory(
|
||||
@Header("Authorization") authHeader: String,
|
||||
@Query("customer_id") customerId: String,
|
||||
@Query("product_instance_id") productInstanceId: String,
|
||||
@Query("limit") limit: Int,
|
||||
@Query("offset") offset: Int,
|
||||
): ApiResponse<VisaTxHistoryResponse>
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.datasource.api.visa
|
||||
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.visa.models.response.GenerateNonceResponse
|
||||
import com.tangem.datasource.api.visa.models.response.JWTResponse
|
||||
import retrofit2.http.Field
|
||||
|
|
@ -27,5 +28,5 @@ interface TangemVisaAuthApi {
|
|||
): JWTResponse
|
||||
|
||||
@POST("auth/refresh_token")
|
||||
suspend fun refreshAccessToken(@Field("refresh_token") refreshToken: String): JWTResponse
|
||||
suspend fun refreshAccessToken(@Field("refresh_token") refreshToken: String): ApiResponse<JWTResponse>
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.datasource.api.visa.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class VisaCustomerInfo(
|
||||
@Json(name = "payment_accounts") val paymentAccounts: List<PaymentAccount>,
|
||||
) {
|
||||
data class PaymentAccount(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
|
||||
@Json(name = "payment_account_address") val paymentAccountAddress: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package com.tangem.datasource.api.visa.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.joda.time.DateTime
|
||||
import java.math.BigDecimal
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class VisaTxHistoryResponse(
|
||||
@Json(name = "card_wallet_address")
|
||||
val cardWalletAddress: String,
|
||||
@Json(name = "transactions")
|
||||
val transactions: List<Transaction>,
|
||||
) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Transaction(
|
||||
@Json(name = "auth_code")
|
||||
val authCode: String?,
|
||||
@Json(name = "billing_amount")
|
||||
val billingAmount: BigDecimal,
|
||||
@Json(name = "billing_currency_code")
|
||||
val billingCurrencyCode: Int,
|
||||
@Json(name = "blockchain_amount")
|
||||
val blockchainAmount: BigDecimal,
|
||||
@Json(name = "blockchain_coin_name")
|
||||
val blockchainCoinName: String,
|
||||
@Json(name = "blockchain_fee")
|
||||
val blockchainFee: BigDecimal,
|
||||
// @Json(name = "local_dt")
|
||||
// val localDate: DateTime?,
|
||||
@Json(name = "merchant_category_code")
|
||||
val merchantCategoryCode: String?,
|
||||
@Json(name = "merchant_city")
|
||||
val merchantCity: String?,
|
||||
@Json(name = "merchant_country_code")
|
||||
val merchantCountryCode: String?,
|
||||
@Json(name = "merchant_name")
|
||||
val merchantName: String?,
|
||||
@Json(name = "requests")
|
||||
val requests: List<Request>,
|
||||
@Json(name = "rrn")
|
||||
val rrn: String?,
|
||||
@Json(name = "transaction_amount")
|
||||
val transactionAmount: BigDecimal,
|
||||
@Json(name = "transaction_currency_code")
|
||||
val transactionCurrencyCode: Int,
|
||||
@Json(name = "transaction_dt")
|
||||
val transactionDt: DateTime,
|
||||
@Json(name = "transaction_id")
|
||||
val transactionId: Long,
|
||||
@Json(name = "transaction_status")
|
||||
val transactionStatus: String,
|
||||
@Json(name = "transaction_type")
|
||||
val transactionType: String,
|
||||
) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Request(
|
||||
@Json(name = "billing_amount")
|
||||
val billingAmount: BigDecimal,
|
||||
@Json(name = "billing_currency_code")
|
||||
val billingCurrencyCode: Int,
|
||||
@Json(name = "blockchain_amount")
|
||||
val blockchainAmount: BigDecimal,
|
||||
@Json(name = "blockchain_fee")
|
||||
val blockchainFee: BigDecimal,
|
||||
@Json(name = "error_code")
|
||||
val errorCode: Int,
|
||||
@Json(name = "request_dt")
|
||||
val requestDt: DateTime,
|
||||
@Json(name = "request_status")
|
||||
val requestStatus: String,
|
||||
@Json(name = "request_type")
|
||||
val requestType: String,
|
||||
@Json(name = "transaction_amount")
|
||||
val transactionAmount: BigDecimal,
|
||||
@Json(name = "transaction_currency_code")
|
||||
val transactionCurrencyCode: Int,
|
||||
@Json(name = "transaction_request_id")
|
||||
val transactionRequestId: Long,
|
||||
@Json(name = "tx_hash")
|
||||
val txHash: String?,
|
||||
@Json(name = "tx_status")
|
||||
val txStatus: String?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -121,6 +121,8 @@ object PreferencesKeys {
|
|||
|
||||
val SEED_FIRST_NOTIFICATION_SHOW_TIME by lazy { longPreferencesKey("seedFirstNotificationTime") }
|
||||
|
||||
val WALLETS_WITH_NFT_ENABLED_KEY by lazy { stringSetPreferencesKey(name = "walletsWithNftEnabled") }
|
||||
|
||||
fun getShouldShowStoriesKey(storyId: String) = booleanPreferencesKey("shouldShowStories_$storyId")
|
||||
|
||||
// region Permission
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.core.decompose.context
|
|||
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.essenty.instancekeeper.getOrCreate
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.navigation.AppNavigationProvider
|
||||
import com.tangem.core.decompose.navigation.DefaultAppNavigationProvider
|
||||
import com.tangem.core.decompose.navigation.DefaultRouter
|
||||
|
|
@ -15,7 +15,7 @@ import kotlinx.coroutines.CoroutineScope
|
|||
class DefaultAppComponentContext(
|
||||
componentContext: ComponentContext,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
override val hiltComponentBuilder: DecomposeComponent.Builder,
|
||||
override val hiltComponentBuilder: ModelComponent.Builder,
|
||||
override val messageSender: UiMessageSender,
|
||||
private val replaceRouter: Router? = null,
|
||||
) : AppComponentContext, ComponentContext by componentContext {
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
package com.tangem.core.decompose.di
|
||||
|
||||
/**
|
||||
* Annotation for marking a dependency as a component scoped.
|
||||
*
|
||||
* This means that the lifecycle of the dependency is limited to the lifecycle of the component it is attached to.
|
||||
*/
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ComponentScoped
|
||||
|
|
@ -8,5 +8,5 @@ interface HiltComponentBuilderOwner {
|
|||
/**
|
||||
* Provides access to the Hilt component builder instance.
|
||||
*/
|
||||
val hiltComponentBuilder: DecomposeComponent.Builder
|
||||
val hiltComponentBuilder: ModelComponent.Builder
|
||||
}
|
||||
|
|
@ -10,11 +10,11 @@ import dagger.hilt.components.SingletonComponent
|
|||
/**
|
||||
* Interface for the Decompose component.
|
||||
*
|
||||
* It is annotated as [ComponentScoped], meaning it has a lifecycle that is scoped to the component.
|
||||
* It is annotated as [ModelScoped], meaning it has a lifecycle that is scoped to the component.
|
||||
*/
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
@DefineComponent(parent = SingletonComponent::class)
|
||||
interface DecomposeComponent {
|
||||
interface ModelComponent {
|
||||
|
||||
/**
|
||||
* Builder interface for the component.
|
||||
|
|
@ -51,6 +51,6 @@ interface DecomposeComponent {
|
|||
*
|
||||
* @return The built Decompose component.
|
||||
*/
|
||||
fun build(): DecomposeComponent
|
||||
fun build(): ModelComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.core.decompose.di
|
||||
|
||||
import javax.inject.Scope
|
||||
|
||||
/**
|
||||
* Annotation for marking a dependency as a model scoped.
|
||||
*
|
||||
* This means that the lifecycle of the dependency is limited to the lifecycle of the component's model
|
||||
*/
|
||||
@Scope
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class ModelScoped
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.core.decompose.ui.UiMessageSender
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.annotation.OverridingMethodsMustInvokeSuper
|
||||
|
||||
/**
|
||||
* Abstract class for a component's model.
|
||||
|
|
@ -29,6 +30,7 @@ abstract class Model : InstanceKeeper.Instance {
|
|||
CoroutineScope(context = dispatchers.mainImmediate + SupervisorJob())
|
||||
}
|
||||
|
||||
@OverridingMethodsMustInvokeSuper
|
||||
override fun onDestroy() {
|
||||
runCatching { modelScope.cancel() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.core.decompose.model
|
|||
import com.arkivanov.essenty.instancekeeper.getOrCreate
|
||||
import com.arkivanov.essenty.instancekeeper.getOrCreateSimple
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.EntryPoints
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -15,7 +15,7 @@ import javax.inject.Provider
|
|||
* It provides a map of model providers.
|
||||
*/
|
||||
@EntryPoint
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
@InstallIn(ModelComponent::class)
|
||||
interface ModelsEntryPoint {
|
||||
|
||||
fun models(): Map<Class<*>, Provider<Model>>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.core.decompose.model
|
|||
/**
|
||||
* Lazy container for [Model] params.
|
||||
*
|
||||
* This contrainer can be accessed by DI because it's provided via [com.tangem.core.decompose.di.DecomposeComponent].
|
||||
* This contrainer can be accessed by DI because it's provided via [com.tangem.core.decompose.di.ModelComponent].
|
||||
* */
|
||||
interface ParamsContainer {
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
/* Core */
|
||||
implementation(projects.core.decompose)
|
||||
|
||||
/* Libs - AndroidX */
|
||||
implementation(deps.lifecycle.runtime.ktx)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.core.deeplink
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
// TODO: Add tests
|
||||
/**
|
||||
|
|
@ -21,17 +19,11 @@ interface DeepLinksRegistry {
|
|||
|
||||
/**
|
||||
* Registers the given [deepLink].
|
||||
*
|
||||
* @see registerWithLifecycle
|
||||
* @see registerWithViewModel
|
||||
*/
|
||||
fun register(deepLink: DeepLink)
|
||||
|
||||
/**
|
||||
* Registers the given [deepLinks].
|
||||
*
|
||||
* @see registerWithLifecycle
|
||||
* @see registerWithViewModel
|
||||
*/
|
||||
fun register(deepLinks: Collection<DeepLink>)
|
||||
|
||||
|
|
@ -50,17 +42,6 @@ interface DeepLinksRegistry {
|
|||
* */
|
||||
fun unregisterByIds(ids: Collection<String>)
|
||||
|
||||
/**
|
||||
* Registers the [deepLinks] when the [owner] is resumed and ensures that they are unregistered when the [owner] is
|
||||
* stopped.
|
||||
*/
|
||||
fun registerWithLifecycle(owner: LifecycleOwner, deepLinks: Collection<DeepLink>)
|
||||
|
||||
/**
|
||||
* Registers the [deepLinks] and ensures that they are unregistered when the [ViewModel] is closed.
|
||||
*/
|
||||
fun registerWithViewModel(viewModel: ViewModel, deepLinks: Collection<DeepLink>)
|
||||
|
||||
/**
|
||||
* Triggers run last launched [Intent] with deeplink handlers that can handle delayed deeplink
|
||||
* after handle [Intent] clear that and second time no intent will be handled
|
||||
|
|
|
|||
|
|
@ -3,11 +3,8 @@ package com.tangem.core.deeplink.impl
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.deeplink.utils.DeepLinksLifecycleObserver
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultDeepLinksRegistry : DeepLinksRegistry {
|
||||
|
|
@ -103,19 +100,6 @@ internal class DefaultDeepLinksRegistry : DeepLinksRegistry {
|
|||
)
|
||||
}
|
||||
|
||||
override fun registerWithLifecycle(owner: LifecycleOwner, deepLinks: Collection<DeepLink>) {
|
||||
val observer = DeepLinksLifecycleObserver(deepLinksRegistry = this, deepLinks)
|
||||
owner.lifecycle.addObserver(observer)
|
||||
}
|
||||
|
||||
override fun registerWithViewModel(viewModel: ViewModel, deepLinks: Collection<DeepLink>) {
|
||||
viewModel.addCloseable {
|
||||
unregister(deepLinks)
|
||||
}
|
||||
|
||||
register(deepLinks)
|
||||
}
|
||||
|
||||
override fun triggerDelayedDeeplink() {
|
||||
if (lastIntent != null) {
|
||||
val intent = lastIntent
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.core.deeplink.utils
|
||||
|
||||
import com.arkivanov.essenty.lifecycle.subscribe
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
|
||||
fun AppComponentContext.registerDeepLinks(registry: DeepLinksRegistry, vararg deepLinks: DeepLink) {
|
||||
registerDeepLinks(registry, deepLinks.toList())
|
||||
}
|
||||
|
||||
fun AppComponentContext.registerDeepLinks(registry: DeepLinksRegistry, deepLinks: Collection<DeepLink>) {
|
||||
lifecycle.subscribe(
|
||||
onCreate = {
|
||||
registry.register(deepLinks)
|
||||
},
|
||||
onDestroy = {
|
||||
registry.unregister(deepLinks)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package com.tangem.core.deeplink.utils
|
||||
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
|
||||
internal class DeepLinksLifecycleObserver(
|
||||
private val deepLinksRegistry: DeepLinksRegistry,
|
||||
private val deepLinks: Collection<DeepLink>,
|
||||
) : DefaultLifecycleObserver {
|
||||
|
||||
override fun onResume(owner: LifecycleOwner) {
|
||||
deepLinksRegistry.register(deepLinks)
|
||||
}
|
||||
|
||||
override fun onPause(owner: LifecycleOwner) {
|
||||
deepLinksRegistry.unregister(deepLinks)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ package com.tangem.core.ui.components
|
|||
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
|
|
@ -27,13 +27,13 @@ fun ResizableText(
|
|||
maxLines: Int = Int.MAX_VALUE,
|
||||
style: TextStyle = LocalTextStyle.current,
|
||||
) {
|
||||
val fontSizeValue = remember { mutableStateOf(fontSizeRange.max.value) }
|
||||
val fontSizeValue = remember { mutableFloatStateOf(fontSizeRange.max.value) }
|
||||
val readyToDraw = remember { mutableStateOf(false) }
|
||||
|
||||
val textState = remember { mutableStateOf(text) }
|
||||
if (textState.value != text) {
|
||||
readyToDraw.value = false
|
||||
fontSizeValue.value = fontSizeRange.max.value
|
||||
fontSizeValue.floatValue = fontSizeRange.max.value
|
||||
textState.value = text
|
||||
}
|
||||
|
||||
|
|
@ -41,18 +41,18 @@ fun ResizableText(
|
|||
text = text,
|
||||
modifier = modifier.drawWithContent { if (readyToDraw.value) drawContent() },
|
||||
color = color,
|
||||
fontSize = fontSizeValue.value.sp,
|
||||
fontSize = fontSizeValue.floatValue.sp,
|
||||
overflow = overflow,
|
||||
softWrap = false,
|
||||
maxLines = maxLines,
|
||||
onTextLayout = {
|
||||
if (it.hasVisualOverflow) {
|
||||
val nextFontSizeValue = fontSizeValue.value - fontSizeRange.step.value
|
||||
val nextFontSizeValue = fontSizeValue.floatValue - fontSizeRange.step.value
|
||||
if (nextFontSizeValue <= fontSizeRange.min.value) {
|
||||
fontSizeValue.value = fontSizeRange.min.value
|
||||
fontSizeValue.floatValue = fontSizeRange.min.value
|
||||
readyToDraw.value = true
|
||||
} else {
|
||||
fontSizeValue.value = nextFontSizeValue * COEFFICIENT
|
||||
fontSizeValue.floatValue = nextFontSizeValue * COEFFICIENT
|
||||
}
|
||||
} else {
|
||||
readyToDraw.value = true
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import androidx.compose.animation.AnimatedVisibility
|
|||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -119,7 +120,7 @@ private fun TangemTextField(
|
|||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions(),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
colors: TangemTextFieldColors = TangemTextFieldsDefault.defaultTextFieldColors,
|
||||
colors: TextFieldColors = TangemTextFieldsDefault.defaultTextFieldColors,
|
||||
size: TangemTextFieldSize = TangemTextFieldSize.Default,
|
||||
onClear: () -> Unit = { onValueChange(TextFieldValue()) },
|
||||
) {
|
||||
|
|
@ -230,7 +231,7 @@ private fun TangemTextFieldWithIcon(
|
|||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions(),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
colors: TangemTextFieldColors = TangemTextFieldsDefault.defaultTextFieldColors,
|
||||
colors: TextFieldColors = TangemTextFieldsDefault.defaultTextFieldColors,
|
||||
size: TangemTextFieldSize = TangemTextFieldSize.Default,
|
||||
onIconClick: () -> Unit = {},
|
||||
) {
|
||||
|
|
@ -326,154 +327,139 @@ private fun TangemTextFieldSize.toShape(): Shape = when (this) {
|
|||
}
|
||||
|
||||
object TangemTextFieldsDefault {
|
||||
val defaultTextFieldColors: TangemTextFieldColors
|
||||
@Composable @Stable get() = TangemTextFieldColors(
|
||||
textColor = TangemTheme.colors.text.primary1,
|
||||
val defaultTextFieldColors: TextFieldColors
|
||||
@Composable @Stable get() = TextFieldColors(
|
||||
focusedTextColor = TangemTheme.colors.text.primary1,
|
||||
errorTextColor = TangemTheme.colors.text.primary1,
|
||||
unfocusedTextColor = TangemTheme.colors.text.primary1,
|
||||
disabledTextColor = TangemTheme.colors.text.disabled,
|
||||
backgroundColor = Color.Transparent,
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
errorContainerColor = Color.Transparent,
|
||||
cursorColor = TangemTheme.colors.icon.primary1,
|
||||
errorCursorColor = TangemTheme.colors.icon.warning,
|
||||
focusedIndicatorColor = TangemTheme.colors.icon.primary1,
|
||||
unfocusedIndicatorColor = TangemTheme.colors.stroke.secondary,
|
||||
disabledIndicatorColor = TangemTheme.colors.stroke.secondary,
|
||||
errorIndicatorColor = TangemTheme.colors.icon.warning,
|
||||
leadingIconColor = TangemTheme.colors.icon.informative,
|
||||
focusedLeadingIconColor = TangemTheme.colors.icon.informative,
|
||||
unfocusedLeadingIconColor = TangemTheme.colors.icon.informative,
|
||||
disabledLeadingIconColor = Color.Transparent,
|
||||
errorLeadingIconColor = TangemTheme.colors.icon.warning,
|
||||
trailingIconColor = TangemTheme.colors.icon.informative,
|
||||
focusedTrailingIconColor = TangemTheme.colors.icon.informative,
|
||||
unfocusedTrailingIconColor = TangemTheme.colors.icon.informative,
|
||||
disabledTrailingIconColor = Color.Transparent,
|
||||
errorTrailingIconColor = TangemTheme.colors.icon.warning,
|
||||
focusedLabelColor = TangemTheme.colors.text.primary1,
|
||||
unfocusedLabelColor = TangemTheme.colors.text.secondary,
|
||||
disabledLabelColor = TangemTheme.colors.text.disabled,
|
||||
errorLabelColor = TangemTheme.colors.icon.warning,
|
||||
placeholderColor = TangemTheme.colors.text.secondary,
|
||||
focusedPlaceholderColor = TangemTheme.colors.text.secondary,
|
||||
errorPlaceholderColor = TangemTheme.colors.text.secondary,
|
||||
unfocusedPlaceholderColor = TangemTheme.colors.text.secondary,
|
||||
disabledPlaceholderColor = TangemTheme.colors.text.disabled,
|
||||
captionColor = TangemTheme.colors.text.tertiary,
|
||||
disabledCaptionColor = TangemTheme.colors.text.disabled,
|
||||
errorCaptionColor = TangemTheme.colors.icon.warning,
|
||||
focusedSupportingTextColor = TangemTheme.colors.text.tertiary,
|
||||
unfocusedSupportingTextColor = TangemTheme.colors.text.tertiary,
|
||||
disabledSupportingTextColor = TangemTheme.colors.text.disabled,
|
||||
errorSupportingTextColor = TangemTheme.colors.icon.warning,
|
||||
textSelectionColors = LocalTextSelectionColors.current,
|
||||
focusedPrefixColor = Color.Transparent,
|
||||
unfocusedPrefixColor = Color.Transparent,
|
||||
disabledPrefixColor = Color.Transparent,
|
||||
errorPrefixColor = Color.Transparent,
|
||||
focusedSuffixColor = Color.Transparent,
|
||||
unfocusedSuffixColor = Color.Transparent,
|
||||
disabledSuffixColor = Color.Transparent,
|
||||
errorSuffixColor = Color.Transparent,
|
||||
)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class TangemTextFieldColors(
|
||||
private val textColor: Color,
|
||||
private val disabledTextColor: Color,
|
||||
private val cursorColor: Color,
|
||||
private val errorCursorColor: Color,
|
||||
private val focusedIndicatorColor: Color,
|
||||
private val unfocusedIndicatorColor: Color,
|
||||
private val errorIndicatorColor: Color,
|
||||
private val disabledIndicatorColor: Color,
|
||||
private val leadingIconColor: Color,
|
||||
private val disabledLeadingIconColor: Color,
|
||||
private val errorLeadingIconColor: Color,
|
||||
private val trailingIconColor: Color,
|
||||
private val disabledTrailingIconColor: Color,
|
||||
private val errorTrailingIconColor: Color,
|
||||
private val backgroundColor: Color,
|
||||
private val focusedLabelColor: Color,
|
||||
private val unfocusedLabelColor: Color,
|
||||
private val disabledLabelColor: Color,
|
||||
private val errorLabelColor: Color,
|
||||
private val placeholderColor: Color,
|
||||
private val disabledPlaceholderColor: Color,
|
||||
private val captionColor: Color,
|
||||
private val disabledCaptionColor: Color,
|
||||
private val errorCaptionColor: Color,
|
||||
) : TextFieldColors {
|
||||
@Composable
|
||||
fun TextFieldColors.leadingIconColor(enabled: Boolean, isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(
|
||||
when {
|
||||
!enabled -> disabledLeadingIconColor
|
||||
isError -> errorLeadingIconColor
|
||||
else -> focusedLeadingIconColor
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun leadingIconColor(enabled: Boolean, isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(
|
||||
when {
|
||||
!enabled -> disabledLeadingIconColor
|
||||
isError -> errorLeadingIconColor
|
||||
else -> leadingIconColor
|
||||
},
|
||||
@Composable
|
||||
fun TextFieldColors.trailingIconColor(enabled: Boolean, isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(
|
||||
when {
|
||||
!enabled -> disabledTrailingIconColor
|
||||
isError -> errorTrailingIconColor
|
||||
else -> focusedTrailingIconColor
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TextFieldColors.indicatorColor(
|
||||
enabled: Boolean,
|
||||
isError: Boolean,
|
||||
interactionSource: InteractionSource,
|
||||
): State<Color> {
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
|
||||
val targetValue = when {
|
||||
!enabled -> disabledIndicatorColor
|
||||
isError -> errorIndicatorColor
|
||||
focused -> focusedIndicatorColor
|
||||
else -> unfocusedIndicatorColor
|
||||
}
|
||||
return if (enabled) {
|
||||
animateColorAsState(
|
||||
targetValue = targetValue,
|
||||
animationSpec = tween(durationMillis = 120),
|
||||
label = "IndicatorColor",
|
||||
)
|
||||
} else {
|
||||
rememberUpdatedState(targetValue)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun trailingIconColor(enabled: Boolean, isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(
|
||||
when {
|
||||
!enabled -> disabledTrailingIconColor
|
||||
isError -> errorTrailingIconColor
|
||||
else -> trailingIconColor
|
||||
},
|
||||
)
|
||||
@Composable
|
||||
fun TextFieldColors.placeholderColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(if (enabled) focusedPlaceholderColor else disabledPlaceholderColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TextFieldColors.labelColor(enabled: Boolean, error: Boolean, interactionSource: InteractionSource): State<Color> {
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
|
||||
val targetValue = when {
|
||||
!enabled -> disabledLabelColor
|
||||
error -> errorLabelColor
|
||||
focused -> focusedLabelColor
|
||||
else -> unfocusedLabelColor
|
||||
}
|
||||
return rememberUpdatedState(targetValue)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun indicatorColor(
|
||||
enabled: Boolean,
|
||||
isError: Boolean,
|
||||
interactionSource: InteractionSource,
|
||||
): State<Color> {
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
@Composable
|
||||
fun TextFieldColors.textColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(if (enabled) focusedTextColor else disabledTextColor)
|
||||
}
|
||||
|
||||
val targetValue = when {
|
||||
!enabled -> disabledIndicatorColor
|
||||
isError -> errorIndicatorColor
|
||||
focused -> focusedIndicatorColor
|
||||
else -> unfocusedIndicatorColor
|
||||
}
|
||||
return if (enabled) {
|
||||
animateColorAsState(
|
||||
targetValue = targetValue,
|
||||
animationSpec = tween(durationMillis = 120),
|
||||
label = "IndicatorColor",
|
||||
)
|
||||
} else {
|
||||
rememberUpdatedState(targetValue)
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
fun TextFieldColors.cursorColor(isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(if (isError) errorCursorColor else cursorColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun backgroundColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(backgroundColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun placeholderColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(if (enabled) placeholderColor else disabledPlaceholderColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun labelColor(enabled: Boolean, error: Boolean, interactionSource: InteractionSource): State<Color> {
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
|
||||
val targetValue = when {
|
||||
!enabled -> disabledLabelColor
|
||||
error -> errorLabelColor
|
||||
focused -> focusedLabelColor
|
||||
else -> unfocusedLabelColor
|
||||
}
|
||||
return rememberUpdatedState(targetValue)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun textColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(if (enabled) textColor else disabledTextColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun cursorColor(isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(if (isError) errorCursorColor else cursorColor)
|
||||
}
|
||||
|
||||
@Suppress("TopLevelComposableFunctions")
|
||||
@Composable
|
||||
fun captionColor(enabled: Boolean, isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(
|
||||
newValue = when {
|
||||
!enabled -> disabledCaptionColor
|
||||
isError -> errorCaptionColor
|
||||
else -> captionColor
|
||||
},
|
||||
)
|
||||
}
|
||||
@Suppress("TopLevelComposableFunctions")
|
||||
@Composable
|
||||
fun TextFieldColors.captionColor(enabled: Boolean, isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(
|
||||
newValue = when {
|
||||
!enabled -> disabledSupportingTextColor
|
||||
isError -> errorSupportingTextColor
|
||||
else -> focusedSupportingTextColor
|
||||
},
|
||||
)
|
||||
}
|
||||
// endregion Defaults
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -105,7 +108,7 @@ private fun CollapsedSearchView(
|
|||
.padding(TangemTheme.dimens.spacing16)
|
||||
.size(TangemTheme.dimens.size24)
|
||||
.clickable { onBackClick() },
|
||||
tint = MaterialTheme.colors.onPrimary,
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
|
|
@ -216,7 +219,8 @@ private fun ExpandedSearchView(
|
|||
onDone = { focusManager.clearFocus() },
|
||||
),
|
||||
colors = TangemTextFieldsDefault.defaultTextFieldColors.copy(
|
||||
placeholderColor = TangemTheme.colors.text.disabled,
|
||||
focusedPlaceholderColor = TangemTheme.colors.text.disabled,
|
||||
unfocusedPlaceholderColor = TangemTheme.colors.text.disabled,
|
||||
cursorColor = TangemTheme.colors.text.tertiary,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.core.ui.components.audits
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package com.tangem.core.ui.components.rows
|
|||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
|
|||
|
|
@ -90,6 +90,9 @@ fun getActiveIconRes(blockchainId: String): Int {
|
|||
"bitrock", "bitrock/test" -> R.drawable.img_bitrock_22
|
||||
"sonic", "sonic/test" -> R.drawable.img_sonic_22
|
||||
"apechain", "apechain/test" -> R.drawable.img_apecoin_22
|
||||
"scroll", "scroll/test" -> R.drawable.ic_alert_24 // FIXME: add icon during full integration
|
||||
"zklink", "zklink/test" -> R.drawable.img_zklink_22
|
||||
"vanar-chain", "vanar-chain/test" -> R.drawable.img_vanar_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -178,6 +181,9 @@ fun getActiveIconResByCoinId(coinId: String): Int {
|
|||
"bitrock", "bitrock/test" -> R.drawable.img_bitrock_22
|
||||
"sonic", "sonic/test" -> R.drawable.img_sonic_22
|
||||
"apechain", "apechain/test" -> R.drawable.img_apecoin_22
|
||||
"scroll", "scroll/test" -> R.drawable.ic_alert_24 // FIXME: add icon during full integration
|
||||
"zklink", "zklink/test" -> R.drawable.img_zklink_22
|
||||
"vanar-chain", "vanar-chain/test" -> R.drawable.img_vanar_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -269,6 +275,9 @@ fun getGreyedOutIconRes(blockchainId: String): Int {
|
|||
"bitrock", "bitrock/test" -> R.drawable.ic_bitrock_22
|
||||
"sonic", "sonic/test" -> R.drawable.ic_sonic_22
|
||||
"apechain", "apechain/test" -> R.drawable.ic_apecoin_22
|
||||
"scroll", "scroll/test" -> R.drawable.ic_alert_24 // FIXME: add icon during full integration
|
||||
"zklink", "zklink/test" -> R.drawable.ic_zklink_22
|
||||
"vanar-chain", "vanar-chain/test" -> R.drawable.ic_vanar_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
9
core/ui/src/main/res/drawable/ic_trash_24.xml
Normal file
9
core/ui/src/main/res/drawable/ic_trash_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M6,19C6,19.53 6.211,20.039 6.586,20.414C6.961,20.789 7.47,21 8,21H16C16.53,21 17.039,20.789 17.414,20.414C17.789,20.039 18,19.53 18,19V7H6V19ZM8,9H16V19H8V9ZM15.5,4L14.5,3H9.5L8.5,4H5V6H19V4H15.5Z"
|
||||
android:fillColor="#1E1E1E"/>
|
||||
</vector>
|
||||
9
core/ui/src/main/res/drawable/ic_vanar_22.xml
Normal file
9
core/ui/src/main/res/drawable/ic_vanar_22.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<path
|
||||
android:pathData="M12.748,18L14.498,14.999L9.25,6H5.749L4,9H7.502L12.748,18ZM16.249,11.999L18,9L16.249,6H12.75L11.001,9H14.5L16.249,11.999Z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
14
core/ui/src/main/res/drawable/ic_zklink_22.xml
Normal file
14
core/ui/src/main/res/drawable/ic_zklink_22.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<path
|
||||
android:pathData="M11.217,3.624L11.002,3.5L10.788,3.624L7.502,5.526L7.288,5.65V5.897V9.455L4.214,11.235L4,11.358V11.605V15.41V15.657L4.214,15.78L7.5,17.683L7.714,17.807L7.929,17.683L11,15.905L14.071,17.683L14.286,17.807L14.5,17.683L17.786,15.78L18,15.657V15.41V11.605V11.358L17.786,11.235L14.716,9.457V5.897V5.65L14.503,5.526L11.217,3.624ZM8.145,6.144L11.002,4.49L13.86,6.144V9.454L11.621,10.75C11.456,10.61 11.243,10.525 11.009,10.525C10.773,10.525 10.557,10.612 10.391,10.755L8.145,9.454V6.144ZM10.068,11.561L7.714,10.198L4.857,11.852V15.163L7.714,16.817L10.571,15.163V12.308C10.295,12.164 10.099,11.886 10.068,11.561ZM11.428,12.318V15.163L14.286,16.817L17.143,15.163V11.852L14.288,10.199L11.951,11.552C11.922,11.888 11.717,12.175 11.428,12.318Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M11.002,7.31L14.716,9.46V13.758L11.002,15.908L7.288,13.758V9.46L11.002,7.31ZM8.145,9.954V13.264L11.002,14.918L13.86,13.264V9.954L11.002,8.299L8.145,9.954Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
19
core/ui/src/main/res/drawable/img_vanar_22.xml
Normal file
19
core/ui/src/main/res/drawable/img_vanar_22.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"/>
|
||||
<path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M0,0h22v22h-22z"
|
||||
android:fillColor="#080A0B"/>
|
||||
<path
|
||||
android:pathData="M12.748,18L14.498,14.999L9.25,6H5.749L4,9H7.502L12.748,18ZM16.249,11.999L18,9L16.249,6H12.75L11.001,9H14.5L16.249,11.999Z"
|
||||
android:fillColor="#03D9AF"/>
|
||||
</group>
|
||||
</vector>
|
||||
24
core/ui/src/main/res/drawable/img_zklink_22.xml
Normal file
24
core/ui/src/main/res/drawable/img_zklink_22.xml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"/>
|
||||
<path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M0,0h22v22h-22z"
|
||||
android:fillColor="#080A0B"/>
|
||||
<path
|
||||
android:pathData="M11.217,3.624L11.002,3.5L10.788,3.624L7.502,5.526L7.288,5.65V5.897V9.455L4.214,11.235L4,11.358V11.605V15.41V15.657L4.214,15.78L7.5,17.683L7.714,17.807L7.929,17.683L11,15.905L14.071,17.683L14.286,17.807L14.5,17.683L17.786,15.78L18,15.657V15.41V11.605V11.358L17.786,11.235L14.716,9.457V5.897V5.65L14.503,5.526L11.217,3.624ZM8.145,6.144L11.002,4.49L13.86,6.144V9.454L11.621,10.75C11.456,10.61 11.243,10.525 11.009,10.525C10.773,10.525 10.557,10.612 10.391,10.755L8.145,9.454V6.144ZM10.068,11.561L7.714,10.198L4.857,11.852V15.163L7.714,16.817L10.571,15.163V12.308C10.295,12.164 10.099,11.886 10.068,11.561ZM11.428,12.318V15.163L14.286,16.817L17.143,15.163V11.852L14.288,10.199L11.951,11.552C11.922,11.888 11.717,12.175 11.428,12.318Z"
|
||||
android:fillColor="#03D498"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M11.002,7.31L14.716,9.46V13.758L11.002,15.908L7.288,13.758V9.46L11.002,7.31ZM8.145,9.954V13.264L11.002,14.918L13.86,13.264V9.954L11.002,8.299L8.145,9.954Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
|
@ -42,4 +42,16 @@ class Debouncer {
|
|||
companion object {
|
||||
const val DEFAULT_WAIT_TIME_MS = 500L
|
||||
}
|
||||
}
|
||||
|
||||
fun CoroutineScope.launchOnCancellation(block: suspend () -> Unit) {
|
||||
launch {
|
||||
try {
|
||||
awaitCancellation()
|
||||
} finally {
|
||||
withContext(NonCancellable) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import dagger.hilt.components.SingletonComponent
|
|||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface CoroutineDispatcherProviderModule {
|
||||
internal interface CoroutineDispatcherProviderModule {
|
||||
|
||||
@Binds
|
||||
fun bindCoroutineDispatcherProvider(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue