Updated on 2026-08-14

This commit is contained in:
Tangem 2025-01-23 14:13:47 +05:00
commit d3068f5818
9 changed files with 195 additions and 48 deletions

View file

@ -5,7 +5,10 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -15,6 +18,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.analytics.Analytics
import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.common.analytics.events.Settings
@ -71,7 +75,7 @@ private fun AddSessionFab(onAddSession: () -> Unit, modifier: Modifier = Modifie
@Composable
private fun EmptyScreen(state: WalletConnectScreenState) {
if (state.isLoading) {
LinearProgressIndicator(
TangemLinearProgressIndicator(
modifier = Modifier.fillMaxWidth(),
color = TangemTheme.colors.icon.accent,
)
@ -102,7 +106,7 @@ private fun EmptyScreen(state: WalletConnectScreenState) {
@Composable
private fun WalletConnectSessions(state: WalletConnectScreenState) {
if (state.isLoading) {
LinearProgressIndicator(
TangemLinearProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.height(2.dp),

View file

@ -0,0 +1,17 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="4dp"/>
<solid android:color="@color/icon_informative" />
</shape>
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%">
<shape>
<corners android:radius="4dp" />
<solid android:color="@color/icon_primary_1" />
</shape>
</scale>
</item>
</layer-list>

View file

@ -4,9 +4,10 @@
android:id="@+id/pb_state"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="20dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:progressBackgroundTint="@color/icon_informative"
android:progressTint="@color/icon_primary_1" />
android:paddingVertical="8dp"
android:indeterminate="false"
android:progressDrawable="@drawable/progress_rounded_shape" />
</merge>

View file

@ -3,11 +3,11 @@
package com.tangem.core.ui.components.progressbar
import android.content.res.Configuration
import androidx.compose.animation.core.*
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
@ -39,7 +39,7 @@ import kotlin.math.abs
* @param strokeCap stroke cap to use for the ends of this progress indicator
*/
@Composable
fun LinearProgressIndicator(
fun TangemLinearProgressIndicator(
progress: () -> Float,
modifier: Modifier = Modifier,
color: Color = TangemTheme.colors.text.accent,
@ -61,6 +61,99 @@ fun LinearProgressIndicator(
}
}
/**
* Progress indicators loading status with infinite animation
*
* @param modifier the [Modifier] to be applied to this progress indicator
* @param color The color of the progress indicator.
* @param backgroundColor The color of the background behind the indicator, visible when the
* progress has not reached that area of the overall indicator yet.
* @param strokeCap stroke cap to use for the ends of this progress indicator
*/
@Suppress("LongMethod")
@Composable
fun TangemLinearProgressIndicator(
modifier: Modifier = Modifier,
color: Color = TangemTheme.colors.text.accent,
backgroundColor: Color = TangemTheme.colors.background.tertiary,
strokeCap: StrokeCap = StrokeCap.Round,
) {
val infiniteTransition = rememberInfiniteTransition()
val firstLineHead by infiniteTransition.animateFloat(
0f,
1f,
infiniteRepeatable(
animation = keyframes {
durationMillis = LINEAR_ANIMATION_DURATION
0f at FIRST_LINE_HEAD_DELAY using FIRST_LINE_HEAD_EASING
1f at FIRST_LINE_HEAD_DURATION + FIRST_LINE_HEAD_DELAY
},
),
)
val firstLineTail by infiniteTransition.animateFloat(
0f,
1f,
infiniteRepeatable(
animation = keyframes {
durationMillis = LINEAR_ANIMATION_DURATION
0f at FIRST_LINE_TAIL_DELAY using FIRST_LINE_TAIL_EASING
1f at FIRST_LINE_TAIL_DURATION + FIRST_LINE_TAIL_DELAY
},
),
)
val secondLineHead by infiniteTransition.animateFloat(
0f,
1f,
infiniteRepeatable(
animation = keyframes {
durationMillis = LINEAR_ANIMATION_DURATION
0f at SECOND_LINE_HEAD_DELAY using SECOND_LINE_HEAD_EASING
1f at SECOND_LINE_HEAD_DURATION + SECOND_LINE_HEAD_DELAY
},
),
)
val secondLineTail by infiniteTransition.animateFloat(
0f,
1f,
infiniteRepeatable(
animation = keyframes {
durationMillis = LINEAR_ANIMATION_DURATION
0f at SECOND_LINE_TAIL_DELAY using SECOND_LINE_TAIL_EASING
1f at SECOND_LINE_TAIL_DURATION + SECOND_LINE_TAIL_DELAY
},
),
)
Canvas(
modifier
.increaseSemanticsBounds()
.semantics(mergeDescendants = true) {
progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate
}
.size(height = 4.dp, width = 24.dp),
) {
val strokeWidth = size.height
drawLinearIndicatorBackground(backgroundColor, strokeWidth, strokeCap)
if (firstLineHead - firstLineTail > 0) {
drawLinearIndicator(
firstLineHead,
firstLineTail,
color,
strokeWidth,
strokeCap,
)
}
if ((secondLineHead - secondLineTail) > 0) {
drawLinearIndicator(
secondLineHead,
secondLineTail,
color,
strokeWidth,
strokeCap,
)
}
}
}
internal fun Modifier.increaseSemanticsBounds(): Modifier {
val padding = 10.dp
return this
@ -129,16 +222,51 @@ private fun DrawScope.drawLinearIndicator(
private fun DrawScope.drawLinearIndicatorBackground(color: Color, strokeWidth: Float, strokeCap: StrokeCap) =
drawLinearIndicator(0f, 1f, color, strokeWidth, strokeCap)
// Indeterminate linear indicator transition specs
// Total duration for one cycle
private const val LINEAR_ANIMATION_DURATION = 1800
// Duration of the head and tail animations for both lines
private const val FIRST_LINE_HEAD_DURATION = 750
private const val FIRST_LINE_TAIL_DURATION = 850
private const val SECOND_LINE_HEAD_DURATION = 567
private const val SECOND_LINE_TAIL_DURATION = 533
// Delay before the start of the head and tail animations for both lines
private const val FIRST_LINE_HEAD_DELAY = 0
private const val FIRST_LINE_TAIL_DELAY = 333
private const val SECOND_LINE_HEAD_DELAY = 1000
private const val SECOND_LINE_TAIL_DELAY = 1267
private val FIRST_LINE_HEAD_EASING = CubicBezierEasing(0.2f, 0f, 0.8f, 1f)
private val FIRST_LINE_TAIL_EASING = CubicBezierEasing(0.4f, 0f, 1f, 1f)
private val SECOND_LINE_HEAD_EASING = CubicBezierEasing(0f, 0f, 0.65f, 1f)
private val SECOND_LINE_TAIL_EASING = CubicBezierEasing(0.1f, 0f, 0.45f, 1f)
// region Preview
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun LinearProgressIndicator_Preview() {
TangemThemePreview {
LinearProgressIndicator(
progress = { 0.5f },
modifier = Modifier.fillMaxWidth(),
)
Column(
modifier = Modifier
.background(TangemTheme.colors.background.secondary)
.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
TangemLinearProgressIndicator(
progress = { 0.6f },
modifier = Modifier.fillMaxWidth(),
color = TangemTheme.colors.icon.primary1,
backgroundColor = TangemTheme.colors.background.tertiary,
)
TangemLinearProgressIndicator(
modifier = Modifier.fillMaxWidth(),
color = TangemTheme.colors.icon.primary1,
backgroundColor = TangemTheme.colors.background.tertiary,
)
}
}
}
// endregion

View file

@ -17,7 +17,7 @@ import com.tangem.core.ui.components.SpacerW8
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.block.information.InformationBlock
import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
import com.tangem.core.ui.components.progressbar.LinearProgressIndicator
import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemAnimations
@ -116,7 +116,7 @@ private fun Content(state: PricePerformanceUM.Value, modifier: Modifier = Modifi
color = TangemTheme.colors.text.tertiary,
)
}
LinearProgressIndicator(
TangemLinearProgressIndicator(
modifier = Modifier
.height(TangemTheme.dimens.size6)
.fillMaxWidth(),

View file

@ -1,5 +1,6 @@
package com.tangem.features.onboarding.v2.stepper.impl.ui
import android.content.res.Configuration
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
@ -15,7 +16,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.appbar.TangemTopAppBar
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.components.progressbar.LinearProgressIndicator
import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemAnimations
import com.tangem.core.ui.res.TangemTheme
@ -59,7 +60,7 @@ internal fun OnboardingStepper(
modifier = modifier,
)
LinearProgressIndicator(
TangemLinearProgressIndicator(
modifier = Modifier
.alpha(progressAlpha)
.padding(horizontal = 16.dp)
@ -67,15 +68,16 @@ internal fun OnboardingStepper(
.fillMaxWidth(),
progress = { animatedIndicatorFraction },
color = TangemTheme.colors.icon.primary1,
backgroundColor = TangemTheme.colors.background.tertiary,
backgroundColor = TangemTheme.colors.icon.primary1.copy(alpha = 0.4f),
strokeCap = StrokeCap.Round,
)
}
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun Preview() {
private fun OnboardingStepper_Preview() {
TangemThemePreview {
Box(
Modifier

View file

@ -1,10 +1,14 @@
package com.tangem.feature.onboarding.api
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.*
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.StrokeCap
import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.onboarding.presentation.wallet2.model.OnboardingSeedPhraseState
import com.tangem.feature.onboarding.presentation.wallet2.ui.*
@ -19,31 +23,22 @@ class OnboardingSeedPhraseScreen : OnboardingSeedPhraseApi {
override fun ScreenContent(uiState: OnboardingSeedPhraseState, subScreen: SeedPhraseScreen, progress: Float) {
BackHandler(onBack = uiState.onBackClick)
Column {
ProgressIndicator(progress)
TangemLinearProgressIndicator(
progress = { progress },
color = TangemTheme.colors.icon.primary1,
backgroundColor = TangemTheme.colors.icon.primary1.copy(alpha = 0.4f),
strokeCap = StrokeCap.Round,
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(top = TangemTheme.dimens.size8)
.padding(horizontal = TangemTheme.dimens.size16),
)
Content(subScreen, uiState)
}
}
}
@Composable
private fun ProgressIndicator(progress: Float) {
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(top = TangemTheme.dimens.size8),
) {
LinearProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = TangemTheme.dimens.size16),
color = TangemTheme.colors.background.action,
backgroundColor = TangemTheme.colors.background.action.copy(alpha = 0.081f),
progress = progress,
)
}
}
@Composable
private fun Content(screen: SeedPhraseScreen, uiState: OnboardingSeedPhraseState) {
when (screen) {

View file

@ -5,9 +5,9 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material3.CircularProgressIndicator
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

View file

@ -8,8 +8,8 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment