mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-09-01 20:08:57 +00:00
backgrounds moved to themes. can be exported/imported
This commit is contained in:
@@ -137,8 +137,6 @@ object ChatModel {
|
||||
val processedCriticalError: ProcessedErrors<AgentErrorType.CRITICAL> = ProcessedErrors(60_000)
|
||||
val processedInternalError: ProcessedErrors<AgentErrorType.INTERNAL> = ProcessedErrors(20_000)
|
||||
|
||||
val backgroundImage: MutableState<ImageBitmap?> by lazy { mutableStateOf(getBackgroundImageOrDefault()) }
|
||||
|
||||
fun getUser(userId: Long): User? = if (currentUser.value?.userId == userId) {
|
||||
currentUser.value
|
||||
} else {
|
||||
|
||||
-13
@@ -175,18 +175,6 @@ class AppPreferences {
|
||||
json.decodeFromString(MapSerializer(String.serializer(), ThemeOverrides.serializer()), it)
|
||||
}, settingsThemes)
|
||||
val profileImageCornerRadius = mkFloatPreference(SHARED_PREFS_PROFILE_IMAGE_CORNER_RADIUS, 22.5f)
|
||||
private val _backgroundImageType = mkStrPreference(SHARED_PREFS_BACKGROUND_IMAGE, null)
|
||||
val backgroundImageType: SharedPreference<BackgroundImageType?> = SharedPreference(
|
||||
get = fun(): BackgroundImageType? {
|
||||
val value = _backgroundImageType.get() ?: return null
|
||||
return try {
|
||||
json.decodeFromString(value)
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
},
|
||||
set = fun(type: BackgroundImageType?) { _backgroundImageType.set(json.encodeToString(type)) }
|
||||
)
|
||||
|
||||
val whatsNewVersion = mkStrPreference(SHARED_PREFS_WHATS_NEW_VERSION, null)
|
||||
val lastMigratedVersionCode = mkIntPreference(SHARED_PREFS_LAST_MIGRATED_VERSION_CODE, 0)
|
||||
@@ -354,7 +342,6 @@ class AppPreferences {
|
||||
private const val SHARED_PREFS_SYSTEM_DARK_THEME = "SystemDarkTheme"
|
||||
private const val SHARED_PREFS_THEMES = "Themes"
|
||||
private const val SHARED_PREFS_PROFILE_IMAGE_CORNER_RADIUS = "ProfileImageCornerRadius"
|
||||
private const val SHARED_PREFS_BACKGROUND_IMAGE = "BackgroundImage"
|
||||
private const val SHARED_PREFS_WHATS_NEW_VERSION = "WhatsNewVersion"
|
||||
private const val SHARED_PREFS_LAST_MIGRATED_VERSION_CODE = "LastMigratedVersionCode"
|
||||
private const val SHARED_PREFS_CUSTOM_DISAPPEARING_MESSAGE_TIME = "CustomDisappearingMessageTime"
|
||||
|
||||
+89
-25
@@ -6,10 +6,9 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.common.model.ChatController
|
||||
import chat.simplex.common.platform.isInNightMode
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.ThemeManager.colorFromReadableHex
|
||||
import chat.simplex.common.ui.theme.ThemeManager.toReadableHex
|
||||
import chat.simplex.common.views.helpers.*
|
||||
@@ -17,12 +16,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import chat.simplex.res.MR
|
||||
import java.io.File
|
||||
|
||||
enum class DefaultTheme {
|
||||
SYSTEM, LIGHT, DARK, SIMPLEX;
|
||||
|
||||
// Call it only with base theme, not SYSTEM
|
||||
fun hasChangedAnyColor(colors: Colors, appColors: AppColors): Boolean {
|
||||
fun hasChangedAnyColor(colors: Colors, appColors: AppColors, appWallpaper: AppWallpaper): Boolean {
|
||||
val palette = when (this) {
|
||||
SYSTEM -> return false
|
||||
LIGHT -> LightColorPalette
|
||||
@@ -41,7 +41,9 @@ enum class DefaultTheme {
|
||||
colors.secondaryVariant != palette.secondaryVariant ||
|
||||
colors.background != palette.background ||
|
||||
colors.surface != palette.surface ||
|
||||
appColors != appPalette
|
||||
appColors != appPalette ||
|
||||
appWallpaper.background != null ||
|
||||
appWallpaper.tint != null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,14 +51,18 @@ data class AppColors(
|
||||
val title: Color,
|
||||
val sentMessage: Color,
|
||||
val receivedMessage: Color,
|
||||
val wallpaperBackground: Color?,
|
||||
val wallpaperTint: Color?,
|
||||
)
|
||||
|
||||
data class AppWallpaper(
|
||||
val background: Color? = null,
|
||||
val tint: Color? = null,
|
||||
val type: BackgroundImageType? = null
|
||||
)
|
||||
|
||||
enum class ThemeColor {
|
||||
PRIMARY, PRIMARY_VARIANT, SECONDARY, SECONDARY_VARIANT, BACKGROUND, SURFACE, TITLE, SENT_MESSAGE, RECEIVED_MESSAGE, WALLPAPER_BACKGROUND, WALLPAPER_TINT;
|
||||
|
||||
fun fromColors(colors: Colors, appColors: AppColors): Color? {
|
||||
fun fromColors(colors: Colors, appColors: AppColors, appWallpaper: AppWallpaper): Color? {
|
||||
return when (this) {
|
||||
PRIMARY -> colors.primary
|
||||
PRIMARY_VARIANT -> colors.primaryVariant
|
||||
@@ -67,8 +73,8 @@ enum class ThemeColor {
|
||||
TITLE -> appColors.title
|
||||
SENT_MESSAGE -> appColors.sentMessage
|
||||
RECEIVED_MESSAGE -> appColors.receivedMessage
|
||||
WALLPAPER_BACKGROUND -> appColors.wallpaperBackground
|
||||
WALLPAPER_TINT -> appColors.wallpaperTint
|
||||
WALLPAPER_BACKGROUND -> appWallpaper.background
|
||||
WALLPAPER_TINT -> appWallpaper.tint
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,8 +108,6 @@ data class ThemeColors(
|
||||
val title: String? = null,
|
||||
val sentMessage: String? = null,
|
||||
val receivedMessage: String? = null,
|
||||
val wallpaperBackground: String? = null,
|
||||
val wallpaperTint: String? = null,
|
||||
) {
|
||||
fun toColors(base: DefaultTheme): Colors {
|
||||
val baseColors = when (base) {
|
||||
@@ -135,8 +139,6 @@ data class ThemeColors(
|
||||
title = title?.colorFromReadableHex() ?: baseColors.title,
|
||||
sentMessage = sentMessage?.colorFromReadableHex() ?: baseColors.sentMessage,
|
||||
receivedMessage = receivedMessage?.colorFromReadableHex() ?: baseColors.receivedMessage,
|
||||
wallpaperBackground = wallpaperBackground?.colorFromReadableHex() ?: baseColors.wallpaperBackground,
|
||||
wallpaperTint = wallpaperTint?.colorFromReadableHex() ?: baseColors.wallpaperTint,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -153,16 +155,79 @@ data class ThemeColors(
|
||||
title = ac.title.toReadableHex(),
|
||||
sentMessage = ac.sentMessage.toReadableHex(),
|
||||
receivedMessage = ac.receivedMessage.toReadableHex(),
|
||||
wallpaperBackground = ac.wallpaperBackground?.toReadableHex(),
|
||||
wallpaperTint = ac.wallpaperTint?.toReadableHex(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ThemeWallpaper (
|
||||
val preset: String? = null,
|
||||
val scale: Float = 1f,
|
||||
val scaleType: BackgroundImageScaleType? = null,
|
||||
val background: String? = null,
|
||||
val tint: String? = null,
|
||||
val image: String? = null,
|
||||
) {
|
||||
fun toAppWallpaper(): AppWallpaper {
|
||||
return AppWallpaper(
|
||||
background = background?.colorFromReadableHex(),
|
||||
tint = tint?.colorFromReadableHex(),
|
||||
type = if (preset != null) {
|
||||
BackgroundImageType.Repeated(filename = preset, scale)
|
||||
} else if (image != null) {
|
||||
BackgroundImageType.Static(
|
||||
filename = image,
|
||||
scale,
|
||||
scaleType ?: BackgroundImageScaleType.FILL
|
||||
)
|
||||
} else
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
fun withFilledWallpaper(): ThemeWallpaper {
|
||||
val aw = toAppWallpaper()
|
||||
return ThemeWallpaper(
|
||||
image = if (aw.type is BackgroundImageType.Static) resizeImageToStrSize(aw.type.image, 5_000_000) else null,
|
||||
preset = if (aw.type is BackgroundImageType.Repeated) aw.type.filename else null,
|
||||
scale = if (aw.type is BackgroundImageType.Repeated) aw.type.scale else (aw.type as BackgroundImageType.Static).scale,
|
||||
scaleType = if (aw.type is BackgroundImageType.Static) aw.type.scaleType else null,
|
||||
background = aw.background?.toReadableHex(),
|
||||
tint = aw.tint?.toReadableHex(),
|
||||
)
|
||||
}
|
||||
|
||||
fun import(): ThemeWallpaper =
|
||||
if (preset == null && image != null) {
|
||||
// Need to save image from string and to save its path
|
||||
try {
|
||||
val parsed = base64ToBitmap(image)
|
||||
val filename = saveBackgroundImage(parsed)
|
||||
copy(image = filename)
|
||||
} catch (e: Exception) {
|
||||
ThemeWallpaper()
|
||||
}
|
||||
} else this
|
||||
|
||||
companion object {
|
||||
fun from(type: BackgroundImageType, background: String?, tint: String?): ThemeWallpaper {
|
||||
return ThemeWallpaper(
|
||||
image = if (type is BackgroundImageType.Static) type.filename else null,
|
||||
preset = if (type is BackgroundImageType.Repeated) type.filename else null,
|
||||
scale = if (type is BackgroundImageType.Repeated) type.scale else (type as BackgroundImageType.Static).scale,
|
||||
scaleType = if (type is BackgroundImageType.Static) type.scaleType else null,
|
||||
background = background,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ThemeOverrides (
|
||||
val base: DefaultTheme,
|
||||
val colors: ThemeColors
|
||||
val colors: ThemeColors,
|
||||
val wallpaper: ThemeWallpaper = ThemeWallpaper(),
|
||||
) {
|
||||
fun withUpdatedColor(name: ThemeColor, color: String?): ThemeOverrides {
|
||||
return copy(colors = when (name) {
|
||||
@@ -175,9 +240,14 @@ data class ThemeOverrides (
|
||||
ThemeColor.TITLE -> colors.copy(title = color)
|
||||
ThemeColor.SENT_MESSAGE -> colors.copy(sentMessage = color)
|
||||
ThemeColor.RECEIVED_MESSAGE -> colors.copy(receivedMessage = color)
|
||||
ThemeColor.WALLPAPER_BACKGROUND -> colors.copy(wallpaperBackground = color)
|
||||
ThemeColor.WALLPAPER_TINT -> colors.copy(wallpaperTint = color)
|
||||
})
|
||||
ThemeColor.WALLPAPER_BACKGROUND -> colors.copy()
|
||||
ThemeColor.WALLPAPER_TINT -> colors.copy()
|
||||
}, wallpaper = when (name) {
|
||||
ThemeColor.WALLPAPER_BACKGROUND -> wallpaper.copy(background = color)
|
||||
ThemeColor.WALLPAPER_TINT -> wallpaper.copy(tint = color)
|
||||
else -> wallpaper.copy()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,8 +295,6 @@ val DarkColorPaletteApp = AppColors(
|
||||
title = SimplexBlue,
|
||||
sentMessage = SentMessageColor,
|
||||
receivedMessage = Color(0x20B1B0B5),
|
||||
wallpaperBackground = null,
|
||||
wallpaperTint = null,
|
||||
)
|
||||
|
||||
val LightColorPalette = lightColors(
|
||||
@@ -246,8 +314,6 @@ val LightColorPaletteApp = AppColors(
|
||||
title = SimplexBlue,
|
||||
sentMessage = SentMessageColor,
|
||||
receivedMessage = Color(0x20B1B0B5),
|
||||
wallpaperBackground = null,
|
||||
wallpaperTint = null,
|
||||
)
|
||||
|
||||
val SimplexColorPalette = darkColors(
|
||||
@@ -268,8 +334,6 @@ val SimplexColorPaletteApp = AppColors(
|
||||
title = Color(0xFF267BE5),
|
||||
sentMessage = SentMessageColor,
|
||||
receivedMessage = Color(0x20B1B0B5),
|
||||
wallpaperBackground = null,
|
||||
wallpaperTint = null,
|
||||
)
|
||||
|
||||
val CurrentColors: MutableStateFlow<ThemeManager.ActiveTheme> = MutableStateFlow(ThemeManager.currentColors(isInNightMode()))
|
||||
|
||||
+27
-12
@@ -8,6 +8,7 @@ import chat.simplex.res.MR
|
||||
import chat.simplex.common.model.AppPreferences
|
||||
import chat.simplex.common.model.ChatController
|
||||
import chat.simplex.common.platform.platform
|
||||
import chat.simplex.common.views.helpers.BackgroundImageType
|
||||
import chat.simplex.common.views.helpers.generalGetString
|
||||
|
||||
// https://github.com/rsms/inter
|
||||
@@ -18,7 +19,7 @@ expect val EmojiFont: FontFamily
|
||||
object ThemeManager {
|
||||
private val appPrefs: AppPreferences = ChatController.appPrefs
|
||||
|
||||
data class ActiveTheme(val name: String, val base: DefaultTheme, val colors: Colors, val appColors: AppColors)
|
||||
data class ActiveTheme(val name: String, val base: DefaultTheme, val colors: Colors, val appColors: AppColors, val wallpaper: AppWallpaper)
|
||||
|
||||
private fun systemDarkThemeColors(): Pair<Colors, DefaultTheme> = when (appPrefs.systemDarkTheme.get()) {
|
||||
DefaultTheme.DARK.name -> DarkColorPalette to DefaultTheme.DARK
|
||||
@@ -43,9 +44,9 @@ object ThemeManager {
|
||||
else -> Triple(DefaultTheme.LIGHT, LightColorPalette, LightColorPaletteApp)
|
||||
}
|
||||
if (theme == null) {
|
||||
return ActiveTheme(themeName, baseTheme.first, baseTheme.second, baseTheme.third)
|
||||
return ActiveTheme(themeName, baseTheme.first, baseTheme.second, baseTheme.third, AppWallpaper())
|
||||
}
|
||||
return ActiveTheme(themeName, baseTheme.first, theme.colors.toColors(theme.base), theme.colors.toAppColors(theme.base))
|
||||
return ActiveTheme(themeName, baseTheme.first, theme.colors.toColors(theme.base), theme.colors.toAppColors(theme.base), theme.wallpaper.toAppWallpaper())
|
||||
}
|
||||
|
||||
fun currentThemeOverridesForExport(darkForSystemTheme: Boolean): ThemeOverrides {
|
||||
@@ -56,8 +57,8 @@ object ThemeManager {
|
||||
if (darkForSystemTheme) appPrefs.systemDarkTheme.get()!! else DefaultTheme.LIGHT.name
|
||||
}
|
||||
val overrides = appPrefs.themeOverrides.get().toMutableMap()
|
||||
val nonFilledTheme = overrides[nonSystemThemeName] ?: ThemeOverrides(base = CurrentColors.value.base, colors = ThemeColors())
|
||||
return nonFilledTheme.copy(colors = nonFilledTheme.colors.withFilledColors(CurrentColors.value.base))
|
||||
val nonFilledTheme = overrides[nonSystemThemeName] ?: ThemeOverrides(base = CurrentColors.value.base, colors = ThemeColors(), wallpaper = ThemeWallpaper())
|
||||
return nonFilledTheme.copy(colors = nonFilledTheme.colors.withFilledColors(CurrentColors.value.base), wallpaper = nonFilledTheme.wallpaper.withFilledWallpaper())
|
||||
}
|
||||
|
||||
// colors, default theme enum, localized name of theme
|
||||
@@ -116,24 +117,38 @@ object ThemeManager {
|
||||
if (colorToSet == null) {
|
||||
// Setting default color from a base theme
|
||||
colorToSet = when(nonSystemThemeName) {
|
||||
DefaultTheme.LIGHT.name -> name.fromColors(LightColorPalette, LightColorPaletteApp)
|
||||
DefaultTheme.DARK.name -> name.fromColors(DarkColorPalette, DarkColorPaletteApp)
|
||||
DefaultTheme.SIMPLEX.name -> name.fromColors(SimplexColorPalette, SimplexColorPaletteApp)
|
||||
DefaultTheme.LIGHT.name -> name.fromColors(LightColorPalette, LightColorPaletteApp, AppWallpaper())
|
||||
DefaultTheme.DARK.name -> name.fromColors(DarkColorPalette, DarkColorPaletteApp, AppWallpaper())
|
||||
DefaultTheme.SIMPLEX.name -> name.fromColors(SimplexColorPalette, SimplexColorPaletteApp, AppWallpaper())
|
||||
// Will not be here
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
val overrides = appPrefs.themeOverrides.get().toMutableMap()
|
||||
val prevValue = overrides[nonSystemThemeName] ?: ThemeOverrides(base = CurrentColors.value.base, colors = ThemeColors())
|
||||
val prevValue = overrides[nonSystemThemeName] ?: ThemeOverrides(base = CurrentColors.value.base, colors = ThemeColors(), wallpaper = ThemeWallpaper())
|
||||
overrides[nonSystemThemeName] = prevValue.withUpdatedColor(name, colorToSet?.toReadableHex())
|
||||
appPrefs.themeOverrides.set(overrides)
|
||||
CurrentColors.value = currentColors(!CurrentColors.value.colors.isLight)
|
||||
}
|
||||
|
||||
fun saveAndApplyBackgroundImage(type: BackgroundImageType?, darkForSystemTheme: Boolean) {
|
||||
val themeName = appPrefs.currentTheme.get()!!
|
||||
val nonSystemThemeName = if (themeName != DefaultTheme.SYSTEM.name) {
|
||||
themeName
|
||||
} else {
|
||||
if (darkForSystemTheme) appPrefs.systemDarkTheme.get()!! else DefaultTheme.LIGHT.name
|
||||
}
|
||||
val overrides = appPrefs.themeOverrides.get().toMutableMap()
|
||||
val prevValue = overrides[nonSystemThemeName] ?: ThemeOverrides(base = CurrentColors.value.base, colors = ThemeColors(), wallpaper = ThemeWallpaper())
|
||||
overrides[nonSystemThemeName] = prevValue.copy(wallpaper = if (type != null) ThemeWallpaper.from(type, prevValue.wallpaper.background, prevValue.wallpaper.tint) else ThemeWallpaper())
|
||||
appPrefs.themeOverrides.set(overrides)
|
||||
CurrentColors.value = currentColors(!CurrentColors.value.colors.isLight)
|
||||
}
|
||||
|
||||
fun saveAndApplyThemeOverrides(theme: ThemeOverrides, darkForSystemTheme: Boolean) {
|
||||
val overrides = appPrefs.themeOverrides.get().toMutableMap()
|
||||
val prevValue = overrides[theme.base.name] ?: ThemeOverrides(base = CurrentColors.value.base, colors = ThemeColors())
|
||||
overrides[theme.base.name] = prevValue.copy(colors = theme.colors)
|
||||
val prevValue = overrides[theme.base.name] ?: ThemeOverrides(base = CurrentColors.value.base, colors = ThemeColors(), wallpaper = ThemeWallpaper())
|
||||
overrides[theme.base.name] = prevValue.copy(colors = theme.colors, wallpaper = theme.wallpaper.import())
|
||||
appPrefs.themeOverrides.set(overrides)
|
||||
appPrefs.currentTheme.set(theme.base.name)
|
||||
CurrentColors.value = currentColors(!CurrentColors.value.colors.isLight)
|
||||
@@ -148,7 +163,7 @@ object ThemeManager {
|
||||
}
|
||||
val overrides = appPrefs.themeOverrides.get().toMutableMap()
|
||||
val prevValue = overrides[nonSystemThemeName] ?: return
|
||||
overrides[nonSystemThemeName] = prevValue.copy(colors = ThemeColors())
|
||||
overrides[nonSystemThemeName] = prevValue.copy(colors = ThemeColors(), wallpaper = prevValue.wallpaper.copy(background = null, tint = null))
|
||||
appPrefs.themeOverrides.set(overrides)
|
||||
CurrentColors.value = currentColors(!CurrentColors.value.colors.isLight)
|
||||
}
|
||||
|
||||
+2
-2
@@ -597,8 +597,8 @@ fun ChatLayout(
|
||||
drawerContentColor = LocalContentColor.current,
|
||||
backgroundColor = Color.Unspecified
|
||||
) { contentPadding ->
|
||||
val backgroundImage = remember { chatModel.backgroundImage }.value
|
||||
val backgroundImageType = remember { appPrefs.backgroundImageType.state }.value
|
||||
val backgroundImage = CurrentColors.collectAsState().value.wallpaper.type?.image
|
||||
val backgroundImageType = CurrentColors.collectAsState().value.wallpaper.type
|
||||
val defaultBackgroundColor = backgroundImageType?.defaultBackgroundColor
|
||||
val defaultTintColor = backgroundImageType?.defaultTintColor
|
||||
BoxWithConstraints(Modifier
|
||||
|
||||
+58
-26
@@ -9,23 +9,28 @@ import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.CurrentColors
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.io.File
|
||||
import kotlin.math.*
|
||||
|
||||
@Serializable
|
||||
enum class PredefinedBackgroundImage(val res: ImageResource, val filename: String, val text: StringResource, val type: BackgroundImageType) {
|
||||
@SerialName("cat") CAT(MR.images.background_cat, "simplex_cat", MR.strings.background_cat, BackgroundImageType.Repeated("simplex_cat", 0.5f)),
|
||||
@SerialName("hearts") HEARTS(MR.images.background_hearts, "simplex_hearts", MR.strings.background_hearts, BackgroundImageType.Repeated("simplex_hearts", 0.5f)),
|
||||
@SerialName("school") SCHOOL(MR.images.background_school, "simplex_school", MR.strings.background_school, BackgroundImageType.Repeated("simplex_school", 0.5f)),
|
||||
@SerialName("internet") INTERNET(MR.images.background_internet, "simplex_internet", MR.strings.background_internet, BackgroundImageType.Repeated("simplex_internet", 0.5f)),
|
||||
@SerialName("space") SPACE(MR.images.background_space, "simplex_space", MR.strings.background_space, BackgroundImageType.Repeated("simplex_space", 0.5f)),
|
||||
@SerialName("pets") PETS(MR.images.background_pets, "simplex_pets", MR.strings.background_pets, BackgroundImageType.Repeated("simplex_pets", 0.5f)),
|
||||
@SerialName("rabbit") RABBIT(MR.images.background_rabbit, "simplex_rabbit", MR.strings.background_rabbit, BackgroundImageType.Repeated("simplex_rabbit", 0.5f));
|
||||
enum class PredefinedBackgroundImage(val res: ImageResource, val filename: String, val scale: Float, val text: StringResource) {
|
||||
@SerialName("cat") CAT(MR.images.background_cat, "simplex_cat", 0.5f, MR.strings.background_cat),
|
||||
@SerialName("hearts") HEARTS(MR.images.background_hearts, "simplex_hearts", 0.5f, MR.strings.background_hearts),
|
||||
@SerialName("school") SCHOOL(MR.images.background_school, "simplex_school", 0.5f, MR.strings.background_school),
|
||||
@SerialName("internet") INTERNET(MR.images.background_internet, "simplex_internet", 0.5f, MR.strings.background_internet),
|
||||
@SerialName("space") SPACE(MR.images.background_space, "simplex_space", 0.5f, MR.strings.background_space),
|
||||
@SerialName("pets") PETS(MR.images.background_pets, "simplex_pets", 0.5f, MR.strings.background_pets),
|
||||
@SerialName("rabbit") RABBIT(MR.images.background_rabbit, "simplex_rabbit", 0.5f, MR.strings.background_rabbit);
|
||||
|
||||
fun toType(): BackgroundImageType =
|
||||
BackgroundImageType.Repeated(filename, scale)
|
||||
|
||||
companion object {
|
||||
fun from(filename: String): PredefinedBackgroundImage? =
|
||||
@@ -34,17 +39,31 @@ enum class PredefinedBackgroundImage(val res: ImageResource, val filename: Strin
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class BackgroundImageScale(val contentScale: ContentScale, val text: StringResource) {
|
||||
@SerialName("crop") CROP(ContentScale.Crop, MR.strings.background_image_scale_crop),
|
||||
enum class BackgroundImageScaleType(val contentScale: ContentScale, val text: StringResource) {
|
||||
@SerialName("fill") FILL(ContentScale.Crop, MR.strings.background_image_scale_fill),
|
||||
@SerialName("fit") FIT(ContentScale.Fit, MR.strings.background_image_scale_fit),
|
||||
@SerialName("fillWidth") FILL_WIDTH(ContentScale.FillWidth, MR.strings.background_image_scale_fill_width),
|
||||
@SerialName("fillHeight") FILL_HEIGHT(ContentScale.FillHeight, MR.strings.background_image_scale_fill_height),
|
||||
@SerialName("fillBounds") FILL_BOUNDS(ContentScale.FillBounds, MR.strings.background_image_scale_fill_bounds)
|
||||
@SerialName("repeat") REPEAT(ContentScale.Fit, MR.strings.background_image_scale_repeat),
|
||||
}
|
||||
|
||||
@Serializable
|
||||
sealed class BackgroundImageType {
|
||||
abstract val filename: String
|
||||
|
||||
val image by lazy {
|
||||
val cache = cachedImage
|
||||
if (cache != null && cache.first == filename) {
|
||||
cache.second
|
||||
} else {
|
||||
val res = if (this is Repeated) {
|
||||
PredefinedBackgroundImage.from(filename)!!.res.toComposeImageBitmap()!!
|
||||
} else {
|
||||
File(getBackgroundImageFilePath(filename)).inputStream().use { loadImageBitmap(it) }
|
||||
}
|
||||
cachedImage = filename to res
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable @SerialName("repeated") data class Repeated(
|
||||
override val filename: String,
|
||||
val scale: Float,
|
||||
@@ -52,34 +71,40 @@ sealed class BackgroundImageType {
|
||||
|
||||
@Serializable @SerialName("static") data class Static(
|
||||
override val filename: String,
|
||||
val scale: BackgroundImageScale,
|
||||
val scale: Float,
|
||||
val scaleType: BackgroundImageScaleType,
|
||||
): BackgroundImageType()
|
||||
|
||||
val background: Color?
|
||||
get() = CurrentColors.value.appColors.wallpaperBackground
|
||||
get() = CurrentColors.value.wallpaper.background
|
||||
|
||||
val tint: Color?
|
||||
get() = CurrentColors.value.appColors.wallpaperTint
|
||||
|
||||
fun toPredefined(): PredefinedBackgroundImage? = PredefinedBackgroundImage.from(filename)
|
||||
get() = CurrentColors.value.wallpaper.tint
|
||||
|
||||
val defaultBackgroundColor: Color
|
||||
@Composable get() = if (this is Static) MaterialTheme.colors.background else MaterialTheme.colors.background
|
||||
@Composable get() = if (this is Static)
|
||||
MaterialTheme.colors.background
|
||||
else
|
||||
MaterialTheme.colors.background
|
||||
|
||||
val defaultTintColor: Color
|
||||
@Composable get() = if (this is Static) MaterialTheme.colors.background.copy(0.9f) else MaterialTheme.colors.primary
|
||||
@Composable get() = if (this is Repeated || (this is Static && this.scaleType == BackgroundImageScaleType.REPEAT))
|
||||
MaterialTheme.colors.primary
|
||||
else
|
||||
MaterialTheme.colors.background.copy(0.9f)
|
||||
|
||||
|
||||
companion object {
|
||||
val default: BackgroundImageType =
|
||||
Repeated(PredefinedBackgroundImage.CAT.filename, 1f)
|
||||
val default: BackgroundImageType
|
||||
get() = PredefinedBackgroundImage.CAT.toType()
|
||||
|
||||
private var cachedImage: Pair<String, ImageBitmap>? = null
|
||||
}
|
||||
}
|
||||
|
||||
fun DrawScope.chatViewBackground(image: ImageBitmap, imageType: BackgroundImageType, defaultBackground: Color, defaultTint: Color) = clipRect {
|
||||
drawRect(imageType.background ?: defaultBackground)
|
||||
if (imageType is BackgroundImageType.Repeated) {
|
||||
val scale = imageType.scale * density
|
||||
fun repeat(imageScale: Float) {
|
||||
val scale = imageScale * density
|
||||
for (h in 0..(size.height / image.height / scale).roundToInt()) {
|
||||
for (w in 0..(size.width / image.width / scale).roundToInt()) {
|
||||
drawImage(
|
||||
@@ -90,8 +115,15 @@ fun DrawScope.chatViewBackground(image: ImageBitmap, imageType: BackgroundImageT
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawRect(imageType.background ?: defaultBackground)
|
||||
if (imageType is BackgroundImageType.Repeated) {
|
||||
repeat(imageType.scale)
|
||||
} else if (imageType is BackgroundImageType.Static && imageType.scaleType == BackgroundImageScaleType.REPEAT) {
|
||||
repeat(imageType.scale)
|
||||
} else if (imageType is BackgroundImageType.Static) {
|
||||
val scale = imageType.scale.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height))
|
||||
val scale = imageType.scaleType.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height))
|
||||
val scaledWidth = (image.width * scale.scaleX).roundToInt()
|
||||
val scaledHeight = (image.height * scale.scaleY).roundToInt()
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight))
|
||||
|
||||
+16
-32
@@ -160,34 +160,6 @@ fun getThemeFromUri(uri: URI, withAlertOnException: Boolean = true): ThemeOverri
|
||||
return null
|
||||
}
|
||||
|
||||
fun getBackgroundImageFromUri(uri: URI, withAlertOnException: Boolean = true): Pair<String, ImageBitmap>? {
|
||||
uri.inputStream().use {
|
||||
runCatching {
|
||||
return uri.toFile().name to loadImageBitmap(it!!)
|
||||
}.onFailure {
|
||||
if (withAlertOnException) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.import_background_image_error),
|
||||
text = generalGetString(MR.strings.import_background_image_desc),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getBackgroundImageOrDefault(filename: String? = null): ImageBitmap? {
|
||||
val type = appPreferences.backgroundImageType.get() ?: return null
|
||||
val res = if (type is BackgroundImageType.Static) {
|
||||
File(getBackgroundImageFilePath(type.filename)).inputStream().use {
|
||||
loadImageBitmap(it)
|
||||
}
|
||||
} else {
|
||||
PredefinedBackgroundImage.from(type.filename)?.res?.toComposeImageBitmap()
|
||||
}
|
||||
return res ?: BackgroundImageType.default.toPredefined()!!.res.toComposeImageBitmap()!!
|
||||
}
|
||||
|
||||
fun saveImage(uri: URI): CryptoFile? {
|
||||
val bitmap = getBitmapFromUri(uri) ?: return null
|
||||
return saveImage(bitmap)
|
||||
@@ -312,9 +284,9 @@ fun saveFileFromUri(uri: URI, withAlertOnException: Boolean = true): CryptoFile?
|
||||
}
|
||||
}
|
||||
|
||||
fun saveBackgroundImage(uri: URI): Pair<String, ImageBitmap>? {
|
||||
val res = getBackgroundImageFromUri(uri, true) ?: return null
|
||||
val destFile = File(getBackgroundImageFilePath(res.first))
|
||||
fun saveBackgroundImage(uri: URI): String? {
|
||||
val destFileName = generateNewFileName("background", "jpg", File(getBackgroundImageFilePath("")))
|
||||
val destFile = File(getBackgroundImageFilePath(destFileName))
|
||||
val inputStream = uri.inputStream()
|
||||
try {
|
||||
Files.copy(inputStream!!, destFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||
@@ -322,9 +294,21 @@ fun saveBackgroundImage(uri: URI): Pair<String, ImageBitmap>? {
|
||||
Log.e(TAG, "Error saving background image: ${e.stackTraceToString()}")
|
||||
return null
|
||||
}
|
||||
return res
|
||||
return destFile.name
|
||||
}
|
||||
|
||||
fun saveBackgroundImage(image: ImageBitmap): String {
|
||||
val destFileName = generateNewFileName("background", "jpg", File(getBackgroundImageFilePath("")))
|
||||
val destFile = File(getBackgroundImageFilePath(destFileName))
|
||||
val dataResized = resizeImageToDataSize(image, false, maxDataSize = 5_000_000)
|
||||
val output = FileOutputStream(destFile)
|
||||
dataResized.use {
|
||||
it.writeTo(output)
|
||||
}
|
||||
return destFile.name
|
||||
}
|
||||
|
||||
// TODO: removes all background for all themes. Should remove only unused
|
||||
fun removeBackgroundImages(except: String? = null) {
|
||||
File(getBackgroundImageFilePath("_")).parentFile.listFiles()?.forEach {
|
||||
if (it.name != except) it.delete()
|
||||
|
||||
+51
-38
@@ -23,7 +23,6 @@ import androidx.compose.ui.unit.*
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.ChatModel
|
||||
@@ -31,7 +30,6 @@ import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.ThemeManager.toReadableHex
|
||||
import chat.simplex.common.views.chat.item.PreviewChatItemView
|
||||
import chat.simplex.res.MR
|
||||
import com.godaddy.android.colorpicker.*
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.serialization.encodeToString
|
||||
import java.net.URI
|
||||
@@ -106,8 +104,8 @@ object AppearanceScope {
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.choose_background_image_title))
|
||||
|
||||
val backgroundImage = remember { chatModel.backgroundImage }.value
|
||||
val backgroundImageType = remember { appPrefs.backgroundImageType.state }.value
|
||||
val backgroundImage = CurrentColors.collectAsState().value.wallpaper.type?.image
|
||||
val backgroundImageType = CurrentColors.collectAsState().value.wallpaper.type
|
||||
if (backgroundImage != null && backgroundImageType != null) {
|
||||
ChatThemePreview(backgroundImage, backgroundImageType)
|
||||
|
||||
@@ -123,14 +121,13 @@ object AppearanceScope {
|
||||
val imageTypeValues = remember {
|
||||
PredefinedBackgroundImage.entries.map { it.filename to generalGetString(it.text) } + ("" to generalGetString(MR.strings.background_choose_own_image))
|
||||
}
|
||||
val systemDark = isSystemInDarkTheme()
|
||||
val importBackgroundImageLauncher = rememberFileChooserLauncher(true) { to: URI? ->
|
||||
if (to != null) {
|
||||
val res = saveBackgroundImage(to)
|
||||
if (res != null) {
|
||||
val (filename, backgroundImage) = res
|
||||
val filename = saveBackgroundImage(to)
|
||||
if (filename != null) {
|
||||
imageTypeState.value = ""
|
||||
chatModel.backgroundImage.value = backgroundImage
|
||||
appPrefs.backgroundImageType.set(BackgroundImageType.Static(filename, BackgroundImageScale.CROP))
|
||||
ThemeManager.saveAndApplyBackgroundImage(BackgroundImageType.Static(filename, 1f, BackgroundImageScaleType.FILL), systemDark)
|
||||
removeBackgroundImages(filename)
|
||||
resetColors()
|
||||
}
|
||||
@@ -145,8 +142,7 @@ object AppearanceScope {
|
||||
withLongRunningApi { importBackgroundImageLauncher.launch("image/*") }
|
||||
} else {
|
||||
imageTypeState.value = filename
|
||||
appPrefs.backgroundImageType.set(PredefinedBackgroundImage.from(filename)!!.type)
|
||||
chatModel.backgroundImage.value = getBackgroundImageOrDefault()
|
||||
ThemeManager.saveAndApplyBackgroundImage(PredefinedBackgroundImage.from(filename)!!.toType(), systemDark)
|
||||
removeBackgroundImages()
|
||||
}
|
||||
}
|
||||
@@ -159,23 +155,37 @@ object AppearanceScope {
|
||||
state.value,
|
||||
valueRange = 0.2f..2f,
|
||||
onValueChange = {
|
||||
appPrefs.backgroundImageType.set(backgroundImageType.copy(scale = it))
|
||||
ThemeManager.saveAndApplyBackgroundImage(backgroundImageType.copy(scale = it), systemDark)
|
||||
}
|
||||
)
|
||||
}
|
||||
} else if (backgroundImageType is BackgroundImageType.Static) {
|
||||
val state = remember(backgroundImageType.scale) { mutableStateOf(backgroundImageType.scale) }
|
||||
val state = remember(backgroundImageType.scaleType) { mutableStateOf(backgroundImageType.scaleType) }
|
||||
val values = remember {
|
||||
BackgroundImageScale.entries.map { it to generalGetString(it.text) }
|
||||
BackgroundImageScaleType.entries.map { it to generalGetString(it.text) }
|
||||
}
|
||||
ExposedDropDownSettingRow(
|
||||
stringResource(MR.strings.background_image_scale),
|
||||
values,
|
||||
state,
|
||||
onSelected = { scale ->
|
||||
appPrefs.backgroundImageType.set(backgroundImageType.copy(scale = scale))
|
||||
onSelected = { scaleType ->
|
||||
ThemeManager.saveAndApplyBackgroundImage(backgroundImageType.copy(scaleType = scaleType), systemDark)
|
||||
}
|
||||
)
|
||||
|
||||
if (backgroundImageType.scaleType == BackgroundImageScaleType.REPEAT) {
|
||||
val state = remember(backgroundImageType.scale) { mutableStateOf(backgroundImageType.scale) }
|
||||
Row {
|
||||
Text("${state.value}", Modifier.width(50.dp))
|
||||
Slider(
|
||||
state.value,
|
||||
valueRange = 0.2f..2f,
|
||||
onValueChange = {
|
||||
ThemeManager.saveAndApplyBackgroundImage(backgroundImageType.copy(scale = it), systemDark)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SectionSpacer()
|
||||
@@ -245,12 +255,16 @@ object AppearanceScope {
|
||||
editColor: (ThemeColor, Color) -> Unit
|
||||
) {
|
||||
val currentTheme by CurrentColors.collectAsState()
|
||||
val systemDark = isSystemInDarkTheme()
|
||||
SectionView(stringResource(MR.strings.settings_section_title_themes)) {
|
||||
val selectedBackground = remember { appPrefs.backgroundImageType.state }.value
|
||||
val selectedBackground = CurrentColors.collectAsState().value.wallpaper.type
|
||||
val cornerRadius = remember { appPreferences.profileImageCornerRadius.state }
|
||||
fun setBackground(type: BackgroundImageType?) {
|
||||
appPrefs.backgroundImageType.set(type)
|
||||
chatModel.backgroundImage.value = getBackgroundImageOrDefault()
|
||||
if (type is BackgroundImageType.Static) {
|
||||
ThemeManager.saveAndApplyThemeColor(ThemeColor.WALLPAPER_BACKGROUND, null, systemDark)
|
||||
ThemeManager.saveAndApplyThemeColor(ThemeColor.WALLPAPER_TINT, null, systemDark)
|
||||
}
|
||||
ThemeManager.saveAndApplyBackgroundImage(type, systemDark)
|
||||
removeBackgroundImages(type?.filename)
|
||||
}
|
||||
@Composable
|
||||
@@ -272,12 +286,12 @@ object AppearanceScope {
|
||||
Modifier
|
||||
.size(width, height)
|
||||
.background(MaterialTheme.colors.background).clip(RoundedCornerShape(percent = cornerRadius.value.roundToInt()))
|
||||
.clickable { setBackground(background?.type) },
|
||||
.clickable { setBackground(background?.toType()) },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (background != null) {
|
||||
val backgroundImage = remember(background.filename) { PredefinedBackgroundImage.from(background.filename)?.res?.toComposeImageBitmap() }
|
||||
ChatThemePreview(backgroundImage, background.type, withMessages = false)
|
||||
ChatThemePreview(backgroundImage, background.toType(), withMessages = false)
|
||||
}
|
||||
if ((background == null && selectedBackground == null) || (selectedBackground?.filename == background?.filename)) {
|
||||
Checked()
|
||||
@@ -289,10 +303,9 @@ object AppearanceScope {
|
||||
fun OwnBackgroundItem(type: BackgroundImageType?) {
|
||||
val importBackgroundImageLauncher = rememberFileChooserLauncher(true) { to: URI? ->
|
||||
if (to != null) {
|
||||
val res = saveBackgroundImage(to)
|
||||
if (res != null) {
|
||||
val (filename, backgroundImage) = res
|
||||
setBackground(BackgroundImageType.Static(filename, BackgroundImageScale.CROP))
|
||||
val filename = saveBackgroundImage(to)
|
||||
if (filename != null) {
|
||||
setBackground(BackgroundImageType.Static(filename, 1f, BackgroundImageScaleType.FILL))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,7 +318,7 @@ object AppearanceScope {
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
val backgroundImage = remember { chatModel.backgroundImage }.value
|
||||
val backgroundImage = CurrentColors.collectAsState().value.wallpaper.type?.image
|
||||
if (type is BackgroundImageType.Static && backgroundImage != null) {
|
||||
ChatThemePreview(backgroundImage, type, withMessages = false)
|
||||
Checked()
|
||||
@@ -322,7 +335,7 @@ object AppearanceScope {
|
||||
BackgroundItem(background)
|
||||
}
|
||||
item {
|
||||
OwnBackgroundItem(remember { appPrefs.backgroundImageType.state }.value)
|
||||
OwnBackgroundItem(CurrentColors.collectAsState().value.wallpaper.type)
|
||||
}
|
||||
}
|
||||
if (appPlatform.isDesktop) {
|
||||
@@ -341,11 +354,11 @@ object AppearanceScope {
|
||||
} else {
|
||||
LazyHorizontalGrid(
|
||||
rows = GridCells.Fixed(1),
|
||||
Modifier.height(70.dp + DEFAULT_PADDING * 2),
|
||||
Modifier.height(120.dp + DEFAULT_PADDING * 2),
|
||||
contentPadding = PaddingValues(DEFAULT_PADDING),
|
||||
horizontalArrangement = Arrangement.spacedBy(DEFAULT_PADDING_HALF),
|
||||
) {
|
||||
gridContent(50.dp, 70.dp)
|
||||
gridContent(80.dp, 120.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,20 +388,20 @@ object AppearanceScope {
|
||||
|
||||
AppBarTitle(stringResource(MR.strings.customize_theme_title))
|
||||
|
||||
val backgroundImageType = remember { appPrefs.backgroundImageType.state }.value
|
||||
val backgroundImage = remember { chatModel.backgroundImage }
|
||||
ChatThemePreview(backgroundImage.value, backgroundImageType)
|
||||
val backgroundImage = CurrentColors.collectAsState().value.wallpaper.type?.image
|
||||
val backgroundImageType = CurrentColors.collectAsState().value.wallpaper.type
|
||||
ChatThemePreview(backgroundImage, backgroundImageType)
|
||||
SectionSpacer()
|
||||
|
||||
if (backgroundImageType != null) {
|
||||
SectionView(stringResource(MR.strings.settings_section_title_background_image).uppercase()) {
|
||||
val wallpaperBackgroundColor = currentTheme.appColors.wallpaperBackground ?: backgroundImageType.defaultBackgroundColor
|
||||
val wallpaperBackgroundColor = currentTheme.wallpaper.background ?: backgroundImageType.defaultBackgroundColor
|
||||
SectionItemViewSpaceBetween({ editColor(ThemeColor.WALLPAPER_BACKGROUND, wallpaperBackgroundColor) }) {
|
||||
val title = generalGetString(MR.strings.color_wallpaper_background)
|
||||
Text(title)
|
||||
Icon(painterResource(MR.images.ic_circle_filled), title, tint = wallpaperBackgroundColor)
|
||||
}
|
||||
val wallpaperTintColor = currentTheme.appColors.wallpaperTint ?: backgroundImageType.defaultTintColor
|
||||
val wallpaperTintColor = currentTheme.wallpaper.tint ?: backgroundImageType.defaultTintColor
|
||||
SectionItemViewSpaceBetween({ editColor(ThemeColor.WALLPAPER_TINT, wallpaperTintColor) }) {
|
||||
val title = generalGetString(MR.strings.color_wallpaper_tint)
|
||||
Text(title)
|
||||
@@ -446,7 +459,7 @@ object AppearanceScope {
|
||||
}
|
||||
}
|
||||
val isInDarkTheme = isInDarkTheme()
|
||||
if (currentTheme.base.hasChangedAnyColor(currentTheme.colors, currentTheme.appColors)) {
|
||||
if (currentTheme.base.hasChangedAnyColor(currentTheme.colors, currentTheme.appColors, currentTheme.wallpaper)) {
|
||||
SectionItemView({ ThemeManager.resetAllThemeColors(darkForSystemTheme = isInDarkTheme) }) {
|
||||
Text(generalGetString(MR.strings.reset_color), color = colors.primary)
|
||||
}
|
||||
@@ -500,9 +513,9 @@ object AppearanceScope {
|
||||
|
||||
val supportedLiveChange = name in listOf(ThemeColor.SECONDARY, ThemeColor.RECEIVED_MESSAGE, ThemeColor.SENT_MESSAGE, ThemeColor.WALLPAPER_BACKGROUND, ThemeColor.WALLPAPER_TINT)
|
||||
if (supportedLiveChange) {
|
||||
val backgroundImageType = remember { appPrefs.backgroundImageType.state }.value
|
||||
val backgroundImage = remember { chatModel.backgroundImage }
|
||||
ChatThemePreview(backgroundImage.value, backgroundImageType)
|
||||
val backgroundImage = CurrentColors.collectAsState().value.wallpaper.type?.image
|
||||
val backgroundImageType = CurrentColors.collectAsState().value.wallpaper.type
|
||||
ChatThemePreview(backgroundImage, backgroundImageType)
|
||||
SectionSpacer()
|
||||
}
|
||||
|
||||
|
||||
@@ -1551,11 +1551,9 @@
|
||||
<string name="background_image_background_color">Background</string>
|
||||
<string name="background_image_tint_color">Tint</string>
|
||||
<string name="background_image_scale">Scale</string>
|
||||
<string name="background_image_scale_crop">Crop</string>
|
||||
<string name="background_image_scale_repeat">Repeat</string>
|
||||
<string name="background_image_scale_fill">Fill</string>
|
||||
<string name="background_image_scale_fit">Fit</string>
|
||||
<string name="background_image_scale_fill_width">Fill width</string>
|
||||
<string name="background_image_scale_fill_height">Fill height</string>
|
||||
<string name="background_image_scale_fill_bounds">Fill bounds</string>
|
||||
|
||||
<!-- Preferences.kt -->
|
||||
<string name="chat_preferences_you_allow">You allow</string>
|
||||
|
||||
Reference in New Issue
Block a user