Updated on 2026-08-14
This commit is contained in:
parent
ba28cf5b6d
commit
7bbab0a40b
14 changed files with 212 additions and 4 deletions
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
</style>
|
||||
|
||||
<style name="SplashTheme" parent="Theme.SplashScreen">
|
||||
<item name="windowSplashScreenBackground">@color/background_primary</item>
|
||||
<item name="windowSplashScreenBackground">@color/background_secondary</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@drawable/inset_splash</item>
|
||||
<item name="postSplashScreenTheme">@style/AppTheme</item>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@
|
|||
"name": "FEED_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "APP_REDESIGN_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "GASLESS_TRANSACTIONS_ENABLED",
|
||||
"version": "5.33.0"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.core.ui
|
||||
|
||||
interface DesignFeatureToggles {
|
||||
val isRedesignEnabled: Boolean
|
||||
}
|
||||
|
|
@ -16,4 +16,6 @@ interface UiDependencies {
|
|||
val globalSnackbarHostState: SnackbarHostState
|
||||
|
||||
val eventMessageHandler: EventMessageHandler
|
||||
|
||||
val designFeatureToggles: DesignFeatureToggles
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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<Boolean>
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<BladeAnimation> {
|
|||
error("No MainBottomSheetColor provided")
|
||||
}
|
||||
|
||||
val LocalHazeState = staticCompositionLocalOf<HazeState> {
|
||||
error("No HazeState provided")
|
||||
}
|
||||
|
||||
val LocalRedesignEnabled = staticCompositionLocalOf<Boolean> {
|
||||
false
|
||||
}
|
||||
|
||||
val LocalPowerSavingState = compositionLocalOf<PowerSavingState> {
|
||||
error("No PowerSavingState provided")
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the dark theme should be used based on the given [AppThemeMode].
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ internal interface TangemPayCardDetailsBlockComponent {
|
|||
val state: StateFlow<TangemPayCardDetailsUM>
|
||||
|
||||
@Composable
|
||||
fun CardDetailsBlockContent(state: TangemPayCardDetailsUM, modifier: Modifier = Modifier)
|
||||
fun CardDetailsBlockContent(state: TangemPayCardDetailsUM, modifier: Modifier)
|
||||
|
||||
data class Params(val params: TangemPayDetailsContainerComponent.Params)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue