diff --git a/app/src/main/java/com/tangem/tap/core/ui/DefaultDesignFeatureToggles.kt b/app/src/main/java/com/tangem/tap/core/ui/DefaultDesignFeatureToggles.kt
new file mode 100644
index 0000000000..ca241efce8
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/core/ui/DefaultDesignFeatureToggles.kt
@@ -0,0 +1,11 @@
+package com.tangem.tap.core.ui
+
+import com.tangem.core.configtoggle.feature.FeatureTogglesManager
+import com.tangem.core.ui.DesignFeatureToggles
+import javax.inject.Inject
+
+class DefaultDesignFeatureToggles @Inject constructor(
+ featureTogglesManager: FeatureTogglesManager,
+) : DesignFeatureToggles {
+ override val isRedesignEnabled: Boolean = featureTogglesManager.isFeatureEnabled("APP_REDESIGN_ENABLED")
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/UiDependenciesModule.kt b/app/src/main/java/com/tangem/tap/di/UiDependenciesModule.kt
index de5f7ef9e3..b5b3c5ea29 100644
--- a/app/src/main/java/com/tangem/tap/di/UiDependenciesModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/UiDependenciesModule.kt
@@ -4,6 +4,7 @@ import androidx.compose.material3.SnackbarHostState
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.ui.DefaultUiMessageSender
import com.tangem.core.decompose.ui.UiMessageSender
+import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.haptic.VibratorHapticManager
import com.tangem.core.ui.message.EventMessageHandler
@@ -23,12 +24,14 @@ internal object UiDependenciesModule {
fun provideUiDependencies(
vibratorHapticManager: VibratorHapticManager,
appThemeModeHolder: AppThemeModeHolder,
+ designFeatureToggles: DesignFeatureToggles,
): UiDependencies {
return object : UiDependencies {
override val vibratorHapticManager = vibratorHapticManager
override val appThemeModeHolder = appThemeModeHolder
override val globalSnackbarHostState: SnackbarHostState = SnackbarHostState()
override val eventMessageHandler: EventMessageHandler = EventMessageHandler()
+ override val designFeatureToggles: DesignFeatureToggles = designFeatureToggles
}
}
diff --git a/app/src/main/java/com/tangem/tap/di/core/ui/CoreUiBindsModule.kt b/app/src/main/java/com/tangem/tap/di/core/ui/CoreUiBindsModule.kt
new file mode 100644
index 0000000000..b02028c5bf
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/di/core/ui/CoreUiBindsModule.kt
@@ -0,0 +1,16 @@
+package com.tangem.tap.di.core.ui
+
+import com.tangem.core.ui.DesignFeatureToggles
+import com.tangem.tap.core.ui.DefaultDesignFeatureToggles
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+
+@Module
+@InstallIn(SingletonComponent::class)
+interface CoreUiBindsModule {
+
+ @Binds
+ fun bindDesignFeatureToggles(impl: DefaultDesignFeatureToggles): DesignFeatureToggles
+}
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index d91dbb1dbe..6ec92245ac 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -16,7 +16,7 @@
diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
index 36923953bd..51984d020a 100644
--- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
+++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
@@ -67,6 +67,10 @@
"name": "FEED_ENABLED",
"version": "undefined"
},
+ {
+ "name": "APP_REDESIGN_ENABLED",
+ "version": "undefined"
+ },
{
"name": "GASLESS_TRANSACTIONS_ENABLED",
"version": "5.33.0"
diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts
index 33e81eb02f..db9bc30c60 100644
--- a/core/ui/build.gradle.kts
+++ b/core/ui/build.gradle.kts
@@ -61,6 +61,16 @@ dependencies {
api(deps.jodatime)
implementation(deps.timber)
implementation(deps.markdown)
+ implementation(deps.haze) {
+ exclude(module = "activity-compose")
+ exclude(module = "activity")
+ exclude(module = "activity-ktx")
+ }
+ implementation(deps.haze.materials) {
+ exclude(module = "activity-compose")
+ exclude(module = "activity")
+ exclude(module = "activity-ktx")
+ }
/** Tests */
testImplementation(deps.test.junit)
diff --git a/core/ui/src/main/java/com/tangem/core/ui/DesignFeatureToggles.kt b/core/ui/src/main/java/com/tangem/core/ui/DesignFeatureToggles.kt
new file mode 100644
index 0000000000..fc8cf40b5c
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/DesignFeatureToggles.kt
@@ -0,0 +1,5 @@
+package com.tangem.core.ui
+
+interface DesignFeatureToggles {
+ val isRedesignEnabled: Boolean
+}
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/UiDependencies.kt b/core/ui/src/main/java/com/tangem/core/ui/UiDependencies.kt
index 0db95df8d1..10c50f5742 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/UiDependencies.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/UiDependencies.kt
@@ -16,4 +16,6 @@ interface UiDependencies {
val globalSnackbarHostState: SnackbarHostState
val eventMessageHandler: EventMessageHandler
+
+ val designFeatureToggles: DesignFeatureToggles
}
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/haze/HazeExt.kt b/core/ui/src/main/java/com/tangem/core/ui/components/haze/HazeExt.kt
new file mode 100644
index 0000000000..779a7a0b55
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/haze/HazeExt.kt
@@ -0,0 +1,57 @@
+package com.tangem.core.ui.components.haze
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import com.tangem.core.ui.res.LocalHazeState
+import com.tangem.core.ui.res.LocalPowerSavingState
+import com.tangem.core.ui.res.LocalRootBackgroundColor
+import dev.chrisbanes.haze.*
+import dev.chrisbanes.haze.materials.CupertinoMaterials
+import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
+
+@OptIn(ExperimentalHazeMaterialsApi::class)
+@Composable
+internal fun ProvideHaze(content: @Composable () -> Unit) {
+ val hazeState = rememberHazeState()
+ CompositionLocalProvider(
+ LocalHazeState provides hazeState,
+ LocalHazeStyle provides CupertinoMaterials.ultraThin(),
+ ) {
+ content()
+ }
+}
+
+/**
+ * Applies a haze effect to the [Modifier] with consideration of global haze settings and power saving mode.
+ *
+ * @param configure A lambda to configure the [HazeEffectScope].
+ * @return A [Modifier] with the configured haze effect applied.
+ */
+@Composable
+fun Modifier.hazeEffectTangem(
+ style: HazeStyle = HazeStyle.Unspecified,
+ configure: HazeEffectScope.() -> Unit = {},
+): Modifier {
+ val powerSavingEnabled = LocalPowerSavingState.current.isPowerSavingModeEnabled.collectAsState()
+ val hazeState = LocalHazeState.current
+ val isGlobalBlurEnabled = hazeState.blurEnabled && !powerSavingEnabled.value
+ val rootBackground by LocalRootBackgroundColor.current
+
+ return hazeEffect(hazeState, style) {
+ fallbackTint = HazeTint(rootBackground)
+ if (isGlobalBlurEnabled) {
+ configure()
+ }
+ blurEnabled = isGlobalBlurEnabled
+ }
+}
+
+/**
+ * Applies a haze source to the [Modifier] using the current global haze state.
+ */
+@Composable
+fun Modifier.hazeSourceTangem(zIndex: Float = 0f, key: Any? = null) =
+ this.hazeSource(LocalHazeState.current, zIndex, key)
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/powersaving/PowerSavingState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/powersaving/PowerSavingState.kt
new file mode 100644
index 0000000000..54a3a42e9b
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/powersaving/PowerSavingState.kt
@@ -0,0 +1,66 @@
+package com.tangem.core.ui.components.powersaving
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.PowerManager
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.compose.LocalLifecycleOwner
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class PowerSavingState(
+ appContext: Context,
+ lifecycle: androidx.lifecycle.Lifecycle,
+) {
+
+ private val powerManager =
+ appContext.getSystemService(Context.POWER_SERVICE) as? PowerManager ?: error("PowerManager not available")
+
+ val isPowerSavingModeEnabled: StateFlow
+ field = MutableStateFlow(powerManager.isPowerSaveMode)
+
+ init {
+ val intentFilter = IntentFilter().apply {
+ addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)
+ }
+
+ val receiver = PowerSavingModeReceiver()
+
+ ContextCompat.registerReceiver(
+ appContext,
+ receiver,
+ intentFilter,
+ ContextCompat.RECEIVER_NOT_EXPORTED,
+ )
+
+ lifecycle.addObserver(object : DefaultLifecycleObserver {
+ override fun onDestroy(owner: LifecycleOwner) {
+ appContext.unregisterReceiver(receiver)
+ }
+ })
+ }
+
+ inner class PowerSavingModeReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (intent?.action == PowerManager.ACTION_POWER_SAVE_MODE_CHANGED) {
+ isPowerSavingModeEnabled.value = powerManager.isPowerSaveMode
+ }
+ }
+ }
+}
+
+@Composable
+internal fun rememberPowerSavingState(): PowerSavingState {
+ val appContext = LocalContext.current.applicationContext
+ val lifecycle = LocalLifecycleOwner.current.lifecycle
+ return remember(appContext, lifecycle) {
+ PowerSavingState(appContext = appContext, lifecycle = lifecycle)
+ }
+}
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt
index a83759bff8..829a8ff0e1 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt
@@ -18,6 +18,8 @@ import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.LocalSystemBarsIconsController
import com.tangem.core.ui.components.SystemBarsIconsController
import com.tangem.core.ui.components.TangemShimmer
+import com.tangem.core.ui.components.powersaving.PowerSavingState
+import com.tangem.core.ui.components.powersaving.rememberPowerSavingState
import com.tangem.core.ui.components.text.BladeAnimation
import com.tangem.core.ui.components.text.rememberBladeAnimation
import com.tangem.core.ui.haptic.DefaultHapticManager
@@ -28,6 +30,7 @@ import com.tangem.core.ui.windowsize.WindowSize
import com.tangem.core.ui.windowsize.rememberWindowSize
import com.tangem.domain.apptheme.model.AppThemeMode
import com.valentinilk.shimmer.Shimmer
+import dev.chrisbanes.haze.HazeState
@OptIn(ExperimentalFoundationApi::class)
@Composable
@@ -54,7 +57,13 @@ fun TangemTheme(
overrideSystemBarColors = overrideSystemBarColors,
typography = typography,
dimens = dimens,
- content = content,
+ content = {
+ if (uiDependencies.designFeatureToggles.isRedesignEnabled) {
+ TangemThemeRedesign { content() }
+ } else {
+ content()
+ }
+ },
)
}
@@ -114,6 +123,7 @@ fun TangemTheme(
LocalWindowSize provides windowSize,
LocalBladeAnimation provides rememberBladeAnimation(),
LocalSystemBarsIconsController provides systemBarsIconsController,
+ LocalPowerSavingState provides rememberPowerSavingState(),
) {
CompositionLocalProvider(
LocalTangemShimmer provides TangemShimmer,
@@ -403,6 +413,18 @@ val LocalBladeAnimation = staticCompositionLocalOf {
error("No MainBottomSheetColor provided")
}
+val LocalHazeState = staticCompositionLocalOf {
+ error("No HazeState provided")
+}
+
+val LocalRedesignEnabled = staticCompositionLocalOf {
+ false
+}
+
+val LocalPowerSavingState = compositionLocalOf {
+ error("No PowerSavingState provided")
+}
+
/**
* Determines whether the dark theme should be used based on the given [AppThemeMode].
*
diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemeRedesign.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemeRedesign.kt
index 8862de2500..efa104abf2 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemeRedesign.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemeRedesign.kt
@@ -2,8 +2,12 @@
package com.tangem.core.ui.res
+import androidx.compose.foundation.layout.Box
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import com.tangem.core.ui.components.haze.ProvideHaze
+import com.tangem.core.ui.components.haze.hazeSourceTangem
/**
* Provides additional theming for redesigned components.
@@ -21,12 +25,17 @@ fun TangemThemeRedesign(content: @Composable () -> Unit) {
colorScheme = tangemColorScheme(colors = themeColors),
) {
CompositionLocalProvider(
+ LocalRedesignEnabled provides true,
LocalTangemColors provides themeColors,
LocalTangemColors2 provides if (LocalIsInDarkTheme.current) darkThemeColors2() else lightThemeColors2(),
LocalTangemTypography2 provides TangemTypography2(InterFamily),
LocalRootBackgroundColor provides remember(rootBackgroundColor) { mutableStateOf(rootBackgroundColor) },
) {
- content()
+ ProvideHaze {
+ Box(Modifier.hazeSourceTangem(zIndex = 1f)) {
+ content()
+ }
+ }
}
}
}
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/TangemPayCardDetailsBlockComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/TangemPayCardDetailsBlockComponent.kt
index 2714c0a662..9fb41015c0 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/TangemPayCardDetailsBlockComponent.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/TangemPayCardDetailsBlockComponent.kt
@@ -12,7 +12,7 @@ internal interface TangemPayCardDetailsBlockComponent {
val state: StateFlow
@Composable
- fun CardDetailsBlockContent(state: TangemPayCardDetailsUM, modifier: Modifier = Modifier)
+ fun CardDetailsBlockContent(state: TangemPayCardDetailsUM, modifier: Modifier)
data class Params(val params: TangemPayDetailsContainerComponent.Params)
}
\ No newline at end of file
diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml
index 6acac86f00..02b196315c 100644
--- a/gradle/dependencies.toml
+++ b/gradle/dependencies.toml
@@ -98,6 +98,7 @@ markdown = "0.7.2"
markdownComposeView = "0.5.4"
usedesk = "4.4.0"
sumsub = "1.38.0"
+haze = "1.7.1"
# endregion Other libraries
# region Tools
@@ -302,4 +303,6 @@ markdown-composeview = { module = "com.github.jeziellago:compose-markdown", vers
usedesk-chat-sdk = { module = "com.github.Usedesk.Android_SDK:chat-sdk", version.ref = "usedesk" }
usedesk-chat-gui = { module = "com.github.Usedesk.Android_SDK:chat-gui", version.ref = "usedesk" }
sumsub-sdk = { module = "com.sumsub.sns:idensic-mobile-sdk", version.ref = "sumsub" }
+haze = { module = "dev.chrisbanes.haze:haze", version.ref = "haze" }
+haze-materials = { module = "dev.chrisbanes.haze:haze-materials", version.ref = "haze" }
# endregion Other